public bool TryGetTextChanged(TextContentChangedEventArgs args, out string replacementText) { // make sure I am not called with my own changes Contract.ThrowIfTrue(this.MyOwnChanges(args)); // initialize out parameter replacementText = null; var trackingSpansAfterEdit = GetActiveSpansForSnapshot(args.After).Select(ss => (Span)ss).ToList(); var normalizedTrackingSpansAfterEdit = new NormalizedSpanCollection(trackingSpansAfterEdit); if (trackingSpansAfterEdit.Count != normalizedTrackingSpansAfterEdit.Count) { // Because of this edit, some spans merged together. We'll abort return false; } // We want to find the single tracking span that encompasses all the edits made. If there // is no such tracking span (or there are multiple), then we consider the change invalid // and we don't return any changed text. If there is only one, then we find the text in // the new document. // // Note there may be multiple intersecting spans in the case where user typing causes // multiple edits to happen. For example, if the user has "Sub" and replaces it with "fu<tab>" // Then there will be multiple edits due to the text change and then the case correction. // However, both edits will be encompassed in one tracking span. var spansTouchedInEdit = new NormalizedSpanCollection(args.Changes.Select(c => c.NewSpan)); var intersection = NormalizedSpanCollection.Intersection(normalizedTrackingSpansAfterEdit, spansTouchedInEdit); var query = from trackingSpan in _trackingSpans let mappedSpan = trackingSpan.GetSpan(args.After) where intersection.All(intersectionSpan => mappedSpan.IntersectsWith(intersectionSpan)) select trackingSpan; var trackingSpansThatIntersect = query.ToList(); if (trackingSpansThatIntersect.Count != 1) { return false; } var singleIntersectingTrackingSpan = trackingSpansThatIntersect.Single(); replacementText = singleIntersectingTrackingSpan.GetText(args.After); return true; }
/// <summary> /// Gets nested outlining regions for buffer /// </summary> protected void Outline() { ITextSnapshot snapshot = Buffer.CurrentSnapshot; SnapshotParser parser = GetSnapshotParser(snapshot); //parsing snapshot TextRegion regionTree = Outliner.ParseBuffer(parser); List <TextRegion> newRegions = GetRegionList(regionTree); List <Span> oldSpans = Regions.ConvertAll(r => r.AsSnapshotSpan().TranslateTo(snapshot, SpanTrackingMode.EdgeExclusive).Span); List <Span> newSpans = newRegions.ConvertAll(r => r.AsSnapshotSpan().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); } this.Snapshot = snapshot; this.Regions = newRegions; if (changeStart <= changeEnd && this.TagsChanged != null) { this.TagsChanged(this, new SnapshotSpanEventArgs( new SnapshotSpan(this.Snapshot, Span.FromBounds(changeStart, changeEnd)))); } FirstOutlining = false; }
private void updateChangedSpans(ITextSnapshot newSnapshot, IList <Region> newRegions) { //determine the changed span, and send a changed event with the new spans IList <Span> oldSpans = new List <Span>(this._regions.Select(r => CodeFoldingTagger.AsSnapshotSpan(r, this._snapshot) .TranslateTo(newSnapshot, SpanTrackingMode.EdgeExclusive) .Span)); IList <Span> newSpans = new List <Span>(newRegions.Select(r => CodeFoldingTagger.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); } this._snapshot = newSnapshot; this._regions = newRegions; if (changeStart <= changeEnd) { if (this.TagsChanged != null) { this.TagsChanged(this, new SnapshotSpanEventArgs(new SnapshotSpan(this._snapshot, Span.FromBounds(changeStart, changeEnd)))); } else { AsmDudeToolsStatic.Output("reparse_Sync: TagsChanged is null"); } } }
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 IProjectionSnapshot Apply(NormalizedSpanCollection spansToElide, NormalizedSpanCollection spansToExpand) { this.applied = true; try { if (spansToElide == null) { spansToElide = NormalizedSpanCollection.Empty; } if (spansToExpand == null) { spansToExpand = NormalizedSpanCollection.Empty; } if (spansToElide.Count > 0 || spansToExpand.Count > 0) { if ((spansToElide.Count > 0) && (spansToElide[spansToElide.Count - 1].End > this.elBuffer.sourceSnapshot.Length)) { throw new ArgumentOutOfRangeException(nameof(spansToElide)); } if ((spansToExpand.Count > 0) && (spansToExpand[spansToExpand.Count - 1].End > this.elBuffer.sourceSnapshot.Length)) { throw new ArgumentOutOfRangeException(nameof(spansToExpand)); } ElisionSourceSpansChangedEventArgs args = this.elBuffer.ApplySpanChanges(spansToElide, spansToExpand); if (args != null) { ElisionSourceSpansChangedEventRaiser raiser = new ElisionSourceSpansChangedEventRaiser(args); this.baseBuffer.group.EnqueueEvents(raiser, this.baseBuffer); raiser.RaiseEvent(this.baseBuffer, true); } this.baseBuffer.editInProgress = false; } else { this.baseBuffer.editInProgress = false; } } finally { this.baseBuffer.group.FinishEdit(); } return(this.elBuffer.currentElisionSnapshot); }
private ElisionSourceSpansChangedEventArgs ApplySpanChanges(NormalizedSpanCollection spansToElide, NormalizedSpanCollection spansToExpand) { ElisionSnapshot beforeSnapshot = this.currentElisionSnapshot; FrugalList <TextChange> textChanges; ElisionMap newContent = this.content.EditSpans(this.sourceSnapshot, spansToElide, spansToExpand, out textChanges); if (newContent != this.content) { this.content = newContent; INormalizedTextChangeCollection normalizedChanges = NormalizedTextChangeCollection.Create(textChanges); SetCurrentVersionAndSnapshot(normalizedChanges); return(new ElisionSourceSpansChangedEventArgs(beforeSnapshot, this.currentElisionSnapshot, spansToElide, spansToExpand, null)); } else { return(null); } }
/// <summary> /// Publishes the tag changes. /// </summary> /// <remarks> /// Once the tags are parsed from the current document, they are passed into this method /// in order to determine if anything has changed. Once the change detection is done, /// it replaces <see cref = "Snapshot" /> for the tagger with the parameter passed in as /// well as the <see cref = "Tags" />. If there were any changes, the <see cref = "E:TagsChanged" /> /// is raised with the combined snapshot of all changes made. /// </remarks> /// <param name = "newSnapshot">The new snapshot.</param> /// <param name = "newTags">The new tags.</param> protected virtual void PublishTagChanges(ITextSnapshot newSnapshot, List <TTokenTag> newTags) { ReadOnlyCollection <TTokenTag> currentTags = Tags; var oldSpans = new List <Span>(currentTags.Select(tag => tag.Span.TranslateTo(newSnapshot, SpanTrackingMode.EdgeExclusive) .Span)); var newSpans = new List <Span>(newTags.Select(tag => tag.Span.Span)); var oldSpanCollection = new NormalizedSpanCollection(oldSpans); var 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); } Snapshot = newSnapshot; Tags = newTags.AsReadOnly(); if (changeStart <= changeEnd) { var snapshot = new SnapshotSpan(newSnapshot, Span.FromBounds(changeStart, changeEnd)); OnTagsChanged(new SnapshotSpanEventArgs(snapshot)); } }
void ReParse() { ITextSnapshot newSnapshot = textBuffer.CurrentSnapshot; // Store off the current spans. var oldSpans = new List <Span>(this.RegionParser.CurlyBraceRegions.Select(r => AsSnapshotSpan(r, this.currentSnapshot) .TranslateTo(newSnapshot, SpanTrackingMode.EdgeExclusive).Span)); this.RegionParser.Parse(newSnapshot); // Determine the changed span, and send a changed event with the new spans var newSpans = new List <Span>(this.RegionParser.CurlyBraceRegions.Select(r => AsSnapshotSpan(r, newSnapshot).Span)); // The changed regions are regions that appear in one set or the other, but not both. var oldSpanCollection = new NormalizedSpanCollection(oldSpans); var newSpanCollection = new NormalizedSpanCollection(newSpans); var 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); } this.currentSnapshot = newSnapshot; if (changeStart <= changeEnd) { this.TagsChanged?.Invoke(this, new SnapshotSpanEventArgs(new SnapshotSpan(this.currentSnapshot, Span.FromBounds(changeStart, changeEnd)))); } }
/// <summary> /// Search spans from [start ... spans.Count-1] for a span that contains point. /// If a span does contain point, return the index + 1 /// If a span does not contain point, return the index of the first span that starts after point (or spans.Count if there are none). /// </summary> private static int IndexOfContainingSpan(NormalizedSpanCollection spans, int point, int start, bool isEndPoint) { int lo = start; int hi = spans.Count; while (lo < hi) { int mid = (lo + hi) / 2; Span s = spans[mid]; if (s.End < point) { lo = mid + 1; } else if (s.Start > point) { hi = mid; } else { //We know s.Start <= point <= s.End // //If point is an endPoint // we want to return mid + 1 if a span ending at point overlaps s (== point != s.Start). Otherwise return mid. // //If point is a startPoint // we want to return mid if a span starting at point overlaps s (== point == s.End). Otherwise return mid + 1. if (isEndPoint) { return((point != s.Start) ? (mid + 1) : mid); } else { return((point == s.End) ? (mid + 1) : mid); } } } return(lo); }
/// <summary> /// Kick off a background search if we don't have current results. Do nothing otherwise. /// </summary> /// <remarks> /// This method can be called from any thread (though it will generally only be called from the UI thread). /// </remarks> public void QueueSearch(NormalizedSnapshotSpanCollection requestedSnapshotSpans) { Debug.Assert(requestedSnapshotSpans.Count > 0); //Check to see if we have completely searched the current version of the text buffer //and quickly abort since there is no point in queuing up another search if we have. var results = _results; //Snapshot results to avoid taking a lock. if (results.Snapshot == _buffer.CurrentSnapshot) { if ((results.SearchedSpans.Count == 1) && (results.SearchedSpans[0].Start == 0) && (results.SearchedSpans[0].Length == results.Snapshot.Length)) { //We've searched the entire snapshot. return; } if (requestedSnapshotSpans[0].Snapshot == results.Snapshot) { NormalizedSpanCollection unsearchedRequest = NormalizedSpanCollection.Difference(requestedSnapshotSpans, results.SearchedSpans); if (unsearchedRequest.Count == 0) { return; } } } lock (_requestQueue) { _requestQueue.Enqueue(requestedSnapshotSpans); if (_requestQueue.Count != 1) { //Request has been queued & we already have an active thread processing requests. return; } } Task.Factory.StartNew(this.ProcessQueue, CancellationToken.None, TaskCreationOptions.PreferFairness, TaskScheduler.Default); }
public bool TryGetTextChanged(TextContentChangedEventArgs args, out string replacementText) { // make sure I am not called with my own changes Contract.ThrowIfTrue(this.MyOwnChanges(args)); // initialize out parameter replacementText = null; var trackingSpansAfterEdit = GetActiveSpansForSnapshot(args.After).Select(ss => (Span)ss).ToList(); var normalizedTrackingSpansAfterEdit = new NormalizedSpanCollection(trackingSpansAfterEdit); if (trackingSpansAfterEdit.Count != normalizedTrackingSpansAfterEdit.Count) { // Because of this edit, some spans merged together. We'll abort return(false); } var spansTouchedInEdit = new NormalizedSpanCollection(args.Changes.Select(c => c.NewSpan)); var intersection = NormalizedSpanCollection.Intersection(normalizedTrackingSpansAfterEdit, spansTouchedInEdit); if (intersection.Count == 0) { // This edit didn't touch any spans, so ignore it return(false); } else if (intersection.Count > 1) { // TODO: figure out what the proper bail is in the new model return(false); } // Good, we touched just one, so let's propagate it var intersectionSpan = intersection.Single(); var singleTrackingSpanTouched = _trackingSpans.Single(ts => ts.GetSpan(args.After).IntersectsWith(intersectionSpan)); replacementText = singleTrackingSpanTouched.GetText(args.After); return(true); }
public bool TryGetTextChanged(TextContentChangedEventArgs args, out string replacementText) { // make sure I am not called with my own changes Contract.ThrowIfTrue(this.MyOwnChanges(args)); // initialize out parameter replacementText = null; var trackingSpansAfterEdit = GetActiveSpansForSnapshot(args.After).Select(ss => (Span)ss).ToList(); var normalizedTrackingSpansAfterEdit = new NormalizedSpanCollection(trackingSpansAfterEdit); if (trackingSpansAfterEdit.Count != normalizedTrackingSpansAfterEdit.Count) { // Because of this edit, some spans merged together. We'll abort return false; } var spansTouchedInEdit = new NormalizedSpanCollection(args.Changes.Select(c => c.NewSpan)); var intersection = NormalizedSpanCollection.Intersection(normalizedTrackingSpansAfterEdit, spansTouchedInEdit); if (intersection.Count == 0) { // This edit didn't touch any spans, so ignore it return false; } else if (intersection.Count > 1) { // TODO: figure out what the proper bail is in the new model return false; } // Good, we touched just one, so let's propagate it var intersectionSpan = intersection.Single(); var singleTrackingSpanTouched = _trackingSpans.Single(ts => ts.GetSpan(args.After).IntersectsWith(intersectionSpan)); replacementText = singleTrackingSpanTouched.GetText(args.After); return true; }
private void OnTextBufferChanged(object sender, TextContentChangedEventArgs args) { AssertIsForeground(); // This might be an event fired due to our own edit if (args.EditTag == s_propagateSpansEditTag || _session._isApplyingEdit) { return; } using (Logger.LogBlock(FunctionId.Rename_OnTextBufferChanged, CancellationToken.None)) { var trackingSpansAfterEdit = new NormalizedSpanCollection(GetEditableSpansForSnapshot(args.After).Select(ss => (Span)ss)); var spansTouchedInEdit = new NormalizedSpanCollection(args.Changes.Select(c => c.NewSpan)); var intersectionSpans = NormalizedSpanCollection.Intersection(trackingSpansAfterEdit, spansTouchedInEdit); if (intersectionSpans.Count == 0) { // In Razor we sometimes get formatting changes during inline rename that // do not intersect with any of our spans. Ideally this shouldn't happen at // all, but if it does happen we can just ignore it. return; } // Cases with invalid identifiers may cause there to be multiple intersection // spans, but they should still all map to a single tracked rename span (e.g. // renaming "two" to "one two three" may be interpreted as two distinct // additions of "one" and "three"). var boundingIntersectionSpan = Span.FromBounds(intersectionSpans.First().Start, intersectionSpans.Last().End); var trackingSpansTouched = GetEditableSpansForSnapshot(args.After).Where(ss => ss.IntersectsWith(boundingIntersectionSpan)); Debug.Assert(trackingSpansTouched.Count() == 1); var singleTrackingSpanTouched = trackingSpansTouched.Single(); _activeSpan = _referenceSpanToLinkedRenameSpanMap.Where(kvp => kvp.Value.TrackingSpan.GetSpan(args.After).Contains(boundingIntersectionSpan)).Single().Key; _session.UndoManager.OnTextChanged(this.ActiveTextView.Selection, singleTrackingSpanTouched); } }
public ElisionBuffer(IProjectionEditResolver resolver, IContentType contentType, ITextBuffer sourceBuffer, NormalizedSpanCollection exposedSpans, ElisionBufferOptions options, ITextDifferencingService textDifferencingService, GuardedOperations guardedOperations) : base(resolver, contentType, textDifferencingService, guardedOperations) { Debug.Assert(sourceBuffer != null); this.sourceBuffer = sourceBuffer; this.sourceSnapshot = sourceBuffer.CurrentSnapshot; Debug.Assert(sourceBuffer is BaseBuffer); BaseBuffer baseSourceBuffer = (BaseBuffer)sourceBuffer; this.eventHook = new WeakEventHook(this, baseSourceBuffer); this.group = baseSourceBuffer.group; this.group.AddMember(this); this.content = new ElisionMap(this.sourceSnapshot, exposedSpans); StringRebuilder newBuilder = StringRebuilder.Empty; for (int i = 0; (i < exposedSpans.Count); ++i) { newBuilder = newBuilder.Append(BufferFactoryService.StringRebuilderFromSnapshotAndSpan(this.sourceSnapshot, exposedSpans[i])); } this.builder = newBuilder; this.elisionOptions = options; this.currentVersion.SetLength(content.Length); this.currentElisionSnapshot = new ElisionSnapshot(this, this.sourceSnapshot, base.currentVersion, this.builder, this.content, (options & ElisionBufferOptions.FillInMappingMode) != 0); this.currentSnapshot = this.currentElisionSnapshot; }
private void OnTextBufferChanged(object sender, TextContentChangedEventArgs args) { AssertIsForeground(); // This might be an event fired due to our own edit if (args.EditTag == s_propagateSpansEditTag || _session._isApplyingEdit) { return; } using (Logger.LogBlock(FunctionId.Rename_OnTextBufferChanged, CancellationToken.None)) { var trackingSpansAfterEdit = new NormalizedSpanCollection(GetEditableSpansForSnapshot(args.After).Select(ss => (Span)ss)); var spansTouchedInEdit = new NormalizedSpanCollection(args.Changes.Select(c => c.NewSpan)); var intersection = NormalizedSpanCollection.Intersection(trackingSpansAfterEdit, spansTouchedInEdit); if (intersection.Count == 0) { // In Razor we sometimes get formatting changes during inline rename that // do not intersect with any of our spans. Ideally this shouldn't happen at // all, but if it does happen we can just ignore it. return; } else if (intersection.Count > 1) { Contract.Fail("we can't allow edits to touch multiple spans"); } var intersectionSpan = intersection.Single(); var singleTrackingSpanTouched = GetEditableSpansForSnapshot(args.After).Single(ss => ss.IntersectsWith(intersectionSpan)); _activeSpan = _referenceSpanToLinkedRenameSpanMap.Where(kvp => kvp.Value.TrackingSpan.GetSpan(args.After).Contains(intersectionSpan)).Single().Key; _session.UndoManager.OnTextChanged(this.ActiveTextview.Selection, singleTrackingSpanTouched); } }
protected internal override NormalizedSpanCollection GetReadOnlyExtentsImplementation(Span span) { // TODO: make something other than dead slow FrugalList <Span> result = new FrugalList <Span>(base.GetReadOnlyExtentsImplementation(span)); IList <SnapshotSpan> restrictionSpans = this.CurrentBaseSnapshot.MapToSourceSnapshotsForRead(span); foreach (SnapshotSpan restrictionSpan in restrictionSpans) { SnapshotSpan?overlapSpan = (restrictionSpan.Span == span) ? restrictionSpan : restrictionSpan.Overlap(span); if (overlapSpan.HasValue) { BaseBuffer baseBuffer = (BaseBuffer)restrictionSpan.Snapshot.TextBuffer; NormalizedSpanCollection sourceExtents = baseBuffer.GetReadOnlyExtents(overlapSpan.Value); foreach (Span sourceExtent in sourceExtents) { result.AddRange(this.CurrentBaseSnapshot.MapFromSourceSnapshot(new SnapshotSpan(restrictionSpan.Snapshot, sourceExtent))); } } } return(new NormalizedSpanCollection(result)); }
void ReParse() { ITextSnapshot newSnapshot = 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) { int regionStart = -1; string text = line.GetText(); //lines that contain a "[" denote the start of a new region. if ((regionStart = text.IndexOf(startHide, StringComparison.Ordinal)) != -1 || (regionStart = text.IndexOf(startHide.Replace(" ", string.Empty), StringComparison.Ordinal)) != -1) { int currentLevel = (currentRegion != null) ? currentRegion.Level : 1; int newLevel; if (!TryGetLevel(text, regionStart, out newLevel)) { 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 = regionStart, PartialParent = currentRegion.PartialParent }; } //this is a new (sub)region else { currentRegion = new PartialRegion() { Level = newLevel, StartLine = line.LineNumber, StartOffset = regionStart, PartialParent = currentRegion }; } } //lines that contain "]" denote the end of a region else if ((regionStart = text.IndexOf(endHide, StringComparison.Ordinal)) != -1 || (regionStart = text.IndexOf(endHide.Replace(" ", string.Empty), StringComparison.Ordinal)) != -1) { int currentLevel = (currentRegion != null) ? currentRegion.Level : 1; int closingLevel; if (!TryGetLevel(text, regionStart, out closingLevel)) { 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; } } } //determine the changed span, and send a changed event with the new spans List <Span> oldSpans = new List <Span>(this.regions.Select(r => AsSnapshotSpan(r, this.snapshot) .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); } this.snapshot = newSnapshot; this.regions = newRegions; if (changeStart <= changeEnd) { if (this.TagsChanged != null) { this.TagsChanged(this, new SnapshotSpanEventArgs( new SnapshotSpan(this.snapshot, Span.FromBounds(changeStart, changeEnd)))); } } }
public IProjectionSnapshot ModifySpans(NormalizedSpanCollection spansToElide, NormalizedSpanCollection spansToExpand) { using (SpanEdit spedit = new SpanEdit(this)) { return(spedit.Apply(spansToElide, spansToExpand)); } }
private void OnTextBufferChanged(object sender, TextContentChangedEventArgs args) { AssertIsForeground(); // This might be an event fired due to our own edit if (args.EditTag == s_propagateSpansEditTag || _session._isApplyingEdit) { return; } using (Logger.LogBlock(FunctionId.Rename_OnTextBufferChanged, CancellationToken.None)) { var trackingSpansAfterEdit = new NormalizedSpanCollection(GetEditableSpansForSnapshot(args.After).Select(ss => (Span)ss)); var spansTouchedInEdit = new NormalizedSpanCollection(args.Changes.Select(c => c.NewSpan)); var intersectionSpans = NormalizedSpanCollection.Intersection(trackingSpansAfterEdit, spansTouchedInEdit); if (intersectionSpans.Count == 0) { // In Razor we sometimes get formatting changes during inline rename that // do not intersect with any of our spans. Ideally this shouldn't happen at // all, but if it does happen we can just ignore it. return; } // Cases with invalid identifiers may cause there to be multiple intersection // spans, but they should still all map to a single tracked rename span (e.g. // renaming "two" to "one two three" may be interpreted as two distinct // additions of "one" and "three"). var boundingIntersectionSpan = Span.FromBounds(intersectionSpans.First().Start, intersectionSpans.Last().End); var trackingSpansTouched = GetEditableSpansForSnapshot(args.After).Where(ss => ss.IntersectsWith(boundingIntersectionSpan)); Contract.Assert(trackingSpansTouched.Count() == 1); var singleTrackingSpanTouched = trackingSpansTouched.Single(); _activeSpan = _referenceSpanToLinkedRenameSpanMap.Where(kvp => kvp.Value.TrackingSpan.GetSpan(args.After).Contains(boundingIntersectionSpan)).Single().Key; _session.UndoManager.OnTextChanged(this.ActiveTextview.Selection, singleTrackingSpanTouched); } }
const int parse_fail = -1; // a keyword hasn't been matched void ReParse() { m_regionStack.Clear(); ITextSnapshot newSnapshot = buffer.CurrentSnapshot; List <Region> newRegions = new List <Region>(); //var classifier = _classifierAggregator.GetClassifier(this.buffer); foreach (var line in newSnapshot.Lines) { /*if(classifier != null) * { * var x = classifier.GetClassificationSpans(new SnapshotSpan(line.Start, line.Length)); * x = x; * } */ /*if(ReParse_Comment(line, newRegions) != parse_fail) continue; * if(m_regionStack.Count > 0 && m_regionStack.Peek().Type == RegionType.RT_Comment) * { * continue; * }*/ if (ReParse_Segment(line, newRegions) == parse_fail) { if (ReParse_Procedure(line, newRegions) == parse_fail) { //ReParse_Condition(line, newRegions); } } } newRegions = newRegions.Where(r => r.EndLine > -1).OrderBy(r => r.StartLine).ToList(); //determine the changed span, and send a changed event with the new spans List <Span> oldSpans = new List <Span>(this.regions.Select(r => AsSnapshotSpan(r, this.snapshot) .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); } this.snapshot = newSnapshot; this.regions = newRegions; if (changeStart <= changeEnd) { ITextSnapshot snap = this.snapshot; if (this.TagsChanged != null) { this.TagsChanged(this, new SnapshotSpanEventArgs( new SnapshotSpan(this.snapshot, Span.FromBounds(changeStart, changeEnd)))); } } }
void ReParse() { ITextSnapshot newSnapshot = buffer.CurrentSnapshot; List <Region> newRegions = new List <Region>(); //keep the current (deepest) partial region, which will have // references to any parent partial regions. List <Region> parsedRegions = new List <Region>(); List <Region> parsedEndRegions = new List <Region>(); bool undercommentscope = false; foreach (var line in newSnapshot.Lines) { int currentpos = 0; string text = line.GetText(); int currentlevel = 0; int regionstart = 0; int length = text.Length; while (true) { if (undercommentscope) { if ((regionstart = text.IndexOf(endComment, currentpos)) != -1) { Region region = parsedRegions.Last(); region.EndLine = line.LineNumber; region.EndLinePos = regionstart; undercommentscope = false; currentpos += regionstart + endComment.Length; } else { break; } } else { /*if((regionstart = text.IndexOf(cancelLine, currentpos)) != -1) * { * break; * } * else*/if ((regionstart = text.IndexOf(startHide, currentpos)) != -1) { parsedRegions.Add(new Region() { StartLine = line.LineNumber, StartLinePos = regionstart, Level = currentlevel }); currentlevel++; currentpos += regionstart + startHide.Length; } else if ((regionstart = text.IndexOf(endHide, currentpos)) != -1) { currentlevel = Math.Max(currentlevel--, 0); //parsedEndRegions.Add(new RegionEx() {EndLine = line.LineNumber, EndLinePos = regionstart, Level = currentlevel}); for (int i = parsedRegions.Count - 1; i >= 0; i--) { Region region = parsedRegions[i]; if (region.EndLinePos == -1) { region.EndLine = line.LineNumber; region.EndLinePos = regionstart; break; } } currentpos += regionstart + endHide.Length; } else if ((regionstart = text.IndexOf(startComment, currentpos)) != -1) { parsedRegions.Add(new Region() { StartLine = line.LineNumber, StartLinePos = regionstart, Level = currentlevel, OutlineLength = startComment.Length }); undercommentscope = true; currentpos += regionstart + startComment.Length; } else { break; } } if (length <= currentpos) { break; } } } List <Span> oldSpansEx = new List <Span>(); foreach (Region r in this.regionsEX) { SnapshotSpan?span = AsSnapshotSpan(r, this.snapshot); if (span != null) { oldSpansEx.Add(span.Value.TranslateTo(newSnapshot, SpanTrackingMode.EdgeExclusive).Span); } } List <Span> newSpansEx = new List <Span>(); foreach (Region r in parsedRegions) { SnapshotSpan?span = AsSnapshotSpan(r, newSnapshot); if (span != null) { oldSpansEx.Add(span.Value.Span); } } NormalizedSpanCollection oldSpanCollection = new NormalizedSpanCollection(oldSpansEx); NormalizedSpanCollection newSpanCollection = new NormalizedSpanCollection(newSpansEx); //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 (newSpansEx.Count > 0) { changeStart = Math.Min(changeStart, newSpansEx[0].Start); changeEnd = Math.Max(changeEnd, newSpansEx[newSpansEx.Count - 1].End); } this.snapshot = newSnapshot; this.regionsEX = parsedRegions; if (changeStart <= changeEnd) { ITextSnapshot snap = this.snapshot; if (this.TagsChanged != null) { this.TagsChanged(this, new SnapshotSpanEventArgs( new SnapshotSpan(this.snapshot, Span.FromBounds(changeStart, changeEnd)))); } } }
private void ReParse() { ITextSnapshot newSnapshot = this.buffer.CurrentSnapshot; var newRegions = new List <SimpleRegion>(); SimpleRegion currentRegion = null; foreach (var line in newSnapshot.Lines) { string text = line.GetText(); var trimText = text.Trim(); var isCommentOfInterest = false; if (trimText == "//" || trimText == "////" || trimText.StartsWith("// ") || trimText.StartsWith("////")) { isCommentOfInterest = true; } if (isCommentOfInterest) { if (currentRegion == null) { currentRegion = new SimpleRegion { StartLine = line.LineNumber, StartOffset = text.IndexOf("//"), }; } } else { if (currentRegion != null) { var endLineNumber = line.LineNumber - 1; if (endLineNumber != currentRegion.StartLine) { currentRegion.EndLine = endLineNumber; newRegions.Add(currentRegion); } currentRegion = null; } } } if (currentRegion != null) { var endLineNumber = newSnapshot.Lines.Count() - 1; if (endLineNumber != currentRegion.StartLine) { currentRegion.EndLine = endLineNumber; newRegions.Add(currentRegion); } } // determine the changed span, and send a changed event with the new spans List <Span> oldSpans = new List <Span>(this.regions.Select(r => AsSnapshotSpan(r, this.snapshot) .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); } this.snapshot = newSnapshot; this.regions = newRegions; if (changeStart <= changeEnd) { ITextSnapshot snap = this.snapshot; this.TagsChanged?.Invoke( this, new SnapshotSpanEventArgs( new SnapshotSpan(this.snapshot, Span.FromBounds(changeStart, changeEnd)))); } }
public Task <object> CreateChangedDocumentPreviewViewAsync(Document oldDocument, Document newDocument, double zoomLevel, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); // Note: We don't use the original buffer that is associated with oldDocument // (and currently open in the editor) for oldBuffer below. This is because oldBuffer // will be used inside a projection buffer inside our inline diff preview below // and platform's implementation currently has a bug where projection buffers // are being leaked. This leak means that if we use the original buffer that is // currently visible in the editor here, the projection buffer span calculation // would be triggered every time user changes some code in this buffer (even though // the diff view would long have been dismissed by the time user edits the code) // resulting in crashes. Instead we create a new buffer from the same content. // TODO: We could use ITextBufferCloneService instead here to clone the original buffer. var oldBuffer = CreateNewBuffer(oldDocument, cancellationToken); var newBuffer = CreateNewBuffer(newDocument, cancellationToken); // Convert the diffs to be line based. // Compute the diffs between the old text and the new. var diffResult = ComputeEditDifferences(oldDocument, newDocument, cancellationToken); // Need to show the spans in the right that are different. // We also need to show the spans that are in conflict. var originalSpans = GetOriginalSpans(diffResult, cancellationToken); var changedSpans = GetChangedSpans(diffResult, cancellationToken); var newRoot = newDocument.GetSyntaxRootAsync(cancellationToken).WaitAndGetResult(cancellationToken); var conflictNodes = newRoot.GetAnnotatedNodesAndTokens(ConflictAnnotation.Kind); var conflictSpans = conflictNodes.Select(n => n.Span.ToSpan()).ToList(); var conflictDescriptions = conflictNodes.SelectMany(n => n.GetAnnotations(ConflictAnnotation.Kind)) .Select(a => ConflictAnnotation.GetDescription(a)) .Distinct(); var warningNodes = newRoot.GetAnnotatedNodesAndTokens(WarningAnnotation.Kind); var warningSpans = warningNodes.Select(n => n.Span.ToSpan()).ToList(); var warningDescriptions = warningNodes.SelectMany(n => n.GetAnnotations(WarningAnnotation.Kind)) .Select(a => WarningAnnotation.GetDescription(a)) .Distinct(); AttachConflictAndWarningAnnotationToBuffer(newBuffer, conflictSpans, warningSpans); var description = conflictSpans.Count == 0 && warningSpans.Count == 0 ? null : string.Join(Environment.NewLine, conflictDescriptions.Concat(warningDescriptions)); var allSpans = new NormalizedSpanCollection(conflictSpans.Concat(warningSpans).Concat(changedSpans)); var originalLineSpans = CreateLineSpans(oldBuffer.CurrentSnapshot, originalSpans, cancellationToken); var changedLineSpans = CreateLineSpans(newBuffer.CurrentSnapshot, allSpans, cancellationToken); if (!originalLineSpans.Any()) { // This means that we have no differences (likely because of conflicts). // In such cases, use the same spans for the left (old) buffer as the right (new) buffer. originalLineSpans = changedLineSpans; } // Create PreviewWorkspaces around the buffers to be displayed on the left and right // so that all IDE services (colorizer, squiggles etc.) light up in these buffers. var leftDocument = oldDocument.Project .RemoveDocument(oldDocument.Id) .AddDocument(oldDocument.Name, oldBuffer.AsTextContainer().CurrentText); var leftWorkspace = new PreviewWorkspace(leftDocument.Project.Solution); leftWorkspace.OpenDocument(leftDocument.Id); var rightWorkspace = new PreviewWorkspace( oldDocument.WithText(newBuffer.AsTextContainer().CurrentText).Project.Solution); rightWorkspace.OpenDocument(newDocument.Id); return(CreateChangedDocumentViewAsync( oldBuffer, newBuffer, description, originalLineSpans, changedLineSpans, leftWorkspace, rightWorkspace, zoomLevel, cancellationToken)); }
void ReParse() { ITextSnapshot newSnapshot = _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; bool comment_started = false; foreach (var line in newSnapshot.Lines) { int regionStart = -1; string linetext = line.GetText(); string ellipsis_ = ""; string end_text_ = ""; string LabelName_ = ""; bool collapsed_ = false; string current_line = linetext.Replace('\t', ' '); current_line = current_line.TrimStart(); bool regionFinished_comment = false; #region comment section int comment_start = current_line.IndexOf("//", StringComparison.Ordinal); if (comment_start == 0 && !comment_started) { regionStart = comment_start; ellipsis_ = linetext.Substring(comment_start + 2, linetext.Length - comment_start - 2).Trim(); comment_started = true; if (ellipsis_ == "") { ellipsis_ = "..."; } } if (comment_start != 0 && comment_started) { regionFinished_comment = true; } #endregion #region labels bool regionFinished_label = false; var label_values = current_line.Split(' '); if (label_values.Length >= 2) { bool labelFound = Dictionary_asm.Labels.Contains(label_values[0]); if (labelFound && label_values[0] != "global" && label_values[0] != "local") { if (label_values[0] != "end") { regionStart = current_line.IndexOf(label_values[0], StringComparison.Ordinal); ellipsis_ = label_values[1].Replace('\"', ' ').Trim(); LabelName_ = ellipsis_; } else { regionFinished_label = true; LabelName_ = label_values[1].Replace('\"', ' ').Trim(); } } } #endregion #region closing the region if (regionFinished_comment || regionFinished_label) { CloseRegion(newRegions, ref currentRegion, ref comment_started, line, linetext, LabelName_, regionFinished_comment); if (regionFinished_comment && regionFinished_label) { CloseRegion(newRegions, ref currentRegion, ref comment_started, line, linetext, LabelName_); } } #endregion #region opening the new region if (regionStart > -1) { currentRegion = OpenRegion(newRegions, currentRegion, line, regionStart, ellipsis_, end_text_, LabelName_, collapsed_); } #endregion } if (comment_started) { CloseRegion(newRegions, ref currentRegion, ref comment_started, newSnapshot.Lines.Last(), newSnapshot.Lines.Last().GetText(), null); } //determine the changed span, and send a changed event with the new spans List <Span> oldSpans = new List <Span>(_regions.Select(r => AsSnapshotSpan(r, _snapshot) .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); } _snapshot = newSnapshot; _regions = newRegions; if (changeStart <= changeEnd) { if (TagsChanged != null) { TagsChanged(this, new SnapshotSpanEventArgs( new SnapshotSpan(_snapshot, Span.FromBounds(changeStart, changeEnd)))); } } }
public Task<object> CreateChangedDocumentPreviewViewAsync(Document oldDocument, Document newDocument, double zoomLevel, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); // Note: We don't use the original buffer that is associated with oldDocument // (and currently open in the editor) for oldBuffer below. This is because oldBuffer // will be used inside a projection buffer inside our inline diff preview below // and platform's implementation currently has a bug where projection buffers // are being leaked. This leak means that if we use the original buffer that is // currently visible in the editor here, the projection buffer span calculation // would be triggered every time user changes some code in this buffer (even though // the diff view would long have been dismissed by the time user edits the code) // resulting in crashes. Instead we create a new buffer from the same content. // TODO: We could use ITextBufferCloneService instead here to clone the original buffer. var oldBuffer = CreateNewBuffer(oldDocument, cancellationToken); var newBuffer = CreateNewBuffer(newDocument, cancellationToken); // Convert the diffs to be line based. // Compute the diffs between the old text and the new. var diffResult = ComputeEditDifferences(oldDocument, newDocument, cancellationToken); // Need to show the spans in the right that are different. // We also need to show the spans that are in conflict. var originalSpans = GetOriginalSpans(diffResult, cancellationToken); var changedSpans = GetChangedSpans(diffResult, cancellationToken); var description = default(string); var allSpans = default(NormalizedSpanCollection); if (newDocument.SupportsSyntaxTree) { var newRoot = newDocument.GetSyntaxRootSynchronously(cancellationToken); var conflictNodes = newRoot.GetAnnotatedNodesAndTokens(ConflictAnnotation.Kind); var conflictSpans = conflictNodes.Select(n => n.Span.ToSpan()).ToList(); var conflictDescriptions = conflictNodes.SelectMany(n => n.GetAnnotations(ConflictAnnotation.Kind)) .Select(a => ConflictAnnotation.GetDescription(a)) .Distinct(); var warningNodes = newRoot.GetAnnotatedNodesAndTokens(WarningAnnotation.Kind); var warningSpans = warningNodes.Select(n => n.Span.ToSpan()).ToList(); var warningDescriptions = warningNodes.SelectMany(n => n.GetAnnotations(WarningAnnotation.Kind)) .Select(a => WarningAnnotation.GetDescription(a)) .Distinct(); var suppressDiagnosticsNodes = newRoot.GetAnnotatedNodesAndTokens(SuppressDiagnosticsAnnotation.Kind); var suppressDiagnosticsSpans = suppressDiagnosticsNodes.Select(n => n.Span.ToSpan()).ToList(); AttachAnnotationsToBuffer(newBuffer, conflictSpans, warningSpans, suppressDiagnosticsSpans); description = conflictSpans.Count == 0 && warningSpans.Count == 0 ? null : string.Join(Environment.NewLine, conflictDescriptions.Concat(warningDescriptions)); allSpans = new NormalizedSpanCollection(conflictSpans.Concat(warningSpans).Concat(changedSpans)); } else { allSpans = new NormalizedSpanCollection(changedSpans); } var originalLineSpans = CreateLineSpans(oldBuffer.CurrentSnapshot, originalSpans, cancellationToken); var changedLineSpans = CreateLineSpans(newBuffer.CurrentSnapshot, allSpans, cancellationToken); if (!originalLineSpans.Any()) { // This means that we have no differences (likely because of conflicts). // In such cases, use the same spans for the left (old) buffer as the right (new) buffer. originalLineSpans = changedLineSpans; } // Create PreviewWorkspaces around the buffers to be displayed on the left and right // so that all IDE services (colorizer, squiggles etc.) light up in these buffers. var leftDocument = oldDocument.Project .RemoveDocument(oldDocument.Id) .AddDocument(oldDocument.Name, oldBuffer.AsTextContainer().CurrentText, oldDocument.Folders, oldDocument.FilePath); var leftWorkspace = new PreviewWorkspace(leftDocument.Project.Solution); leftWorkspace.OpenDocument(leftDocument.Id); var rightWorkspace = new PreviewWorkspace( newDocument.WithText(newBuffer.AsTextContainer().CurrentText).Project.Solution); rightWorkspace.OpenDocument(newDocument.Id); return CreateChangedDocumentViewAsync( oldBuffer, newBuffer, description, originalLineSpans, changedLineSpans, leftWorkspace, rightWorkspace, zoomLevel, cancellationToken); }
void ReParse() { ITextSnapshot newSnapshot = buffer.CurrentSnapshot; List <Region> newRegions = new List <Region>(); PartialRegion currentRegion = null; foreach (var line in newSnapshot.Lines) { int regionStart = -1; string text = line.GetText(); if ((regionStart = text.IndexOf(Constants.BlockStartHide, StringComparison.Ordinal)) != -1) { int currentLevel = (currentRegion != null) ? currentRegion.Level : 1; if (!TryGetLevel(text, regionStart, out int newLevel)) { newLevel = currentLevel + 1; } 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 = regionStart, PartialParent = currentRegion.PartialParent }; } else { currentRegion = new PartialRegion() { Level = newLevel, StartLine = line.LineNumber, StartOffset = regionStart, PartialParent = currentRegion }; } } else if ((regionStart = text.IndexOf(Constants.BlockEndHide, StringComparison.Ordinal)) != -1) { int currentLevel = (currentRegion != null) ? currentRegion.Level : 1; if (!TryGetLevel(text, regionStart, out int closingLevel)) { closingLevel = currentLevel; } if (currentRegion != null && currentLevel == closingLevel) { newRegions.Add(new Region() { Level = currentLevel, StartLine = currentRegion.StartLine, StartOffset = currentRegion.StartOffset, EndLine = line.LineNumber }); currentRegion = currentRegion.PartialParent; } } } List <Span> oldSpans = new List <Span>(this.regions.Select(r => AsSnapshotSpan(r, this.snapshot) .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); 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); } this.snapshot = newSnapshot; this.regions = newRegions; if (changeStart <= changeEnd) { ITextSnapshot snap = this.snapshot; this.TagsChanged?.Invoke(this, new SnapshotSpanEventArgs(new SnapshotSpan(this.snapshot, Span.FromBounds(changeStart, changeEnd)))); } }
internal void DecomposeSpans() { // Prepare spans for diffing. The basic idea is this: suppose we have input spans from some source snapshot as follows: // // deleted: 0..10 // inserted: 0..3 7..10 // // We would like to raise a text change event that indicates that the text from 3..7 was deleted, rather than // an event indicating that all the text from 0..10 was deleted and replaced. We could simply compute a string // difference of the before & after text, but there might be a lot of text (so that would be expensive), and we // also don't want to suppress eventing when identical text comes from different source buffers (which might have // different content types). So, this routine converts the input spans into a form suitable for diffing: // // deleted: 0..3 3..7 7..10 // inserted 0..3 7..10 // // then we compute the differences of the spans qua spans, which in this case will indicate that the span named "3..7" // was deleted, and that's what we use to generate text change events. // what to substitute for input spans during diffing this.deletedSurrogates = new List <SnapshotSpan> [this.inputDeletedSnapSpans.Count]; this.insertedSurrogates = new List <SnapshotSpan> [this.inputInsertedSnapSpans.Count]; // collect spans by text buffer Dictionary <ITextSnapshot, List <Thing> > buffer2DeletedThings = new Dictionary <ITextSnapshot, List <Thing> >(); for (int ds = 0; ds < this.inputDeletedSnapSpans.Count; ++ds) { SnapshotSpan ss = this.inputDeletedSnapSpans[ds]; List <Thing> things; if (!buffer2DeletedThings.TryGetValue(ss.Snapshot, out things)) { things = new List <Thing>(); buffer2DeletedThings.Add(ss.Snapshot, things); } things.Add(new Thing(ss.Span, ds)); // unrelated deletedSurrogates[ds] = new List <SnapshotSpan>(); } Dictionary <ITextSnapshot, List <Thing> > buffer2InsertedThings = new Dictionary <ITextSnapshot, List <Thing> >(); for (int ns = 0; ns < this.inputInsertedSnapSpans.Count; ++ns) { SnapshotSpan ss = this.inputInsertedSnapSpans[ns]; List <Thing> things; if (!buffer2InsertedThings.TryGetValue(ss.Snapshot, out things)) { things = new List <Thing>(); buffer2InsertedThings.Add(ss.Snapshot, things); } things.Add(new Thing(ss.Span, ns)); // unrelated insertedSurrogates[ns] = new List <SnapshotSpan>(); } foreach (KeyValuePair <ITextSnapshot, List <Thing> > pair in buffer2DeletedThings) { List <Thing> insertedThings; ITextSnapshot snapshot = pair.Key; if (buffer2InsertedThings.TryGetValue(snapshot, out insertedThings)) { List <Thing> deletedThings = pair.Value; insertedThings.Sort(Comparison); deletedThings.Sort(Comparison); int i = 0; int d = 0; do { Span inserted = insertedThings[i].span; Span deleted = deletedThings[d].span; Span?overlap = inserted.Overlap(deleted); if (overlap == null) { if (inserted.Start < deleted.Start) { i++; } else { d++; } } else { NormalizedSpanCollection insertedResidue = NormalizedSpanCollection.Difference(new NormalizedSpanCollection(inserted), new NormalizedSpanCollection(overlap.Value)); // todo add overload to normalizedspancollection if (insertedResidue.Count > 0) { int pos = insertedThings[i].position; insertedThings.RemoveAt(i); bool didOverlap = false; int ir = 0; while (ir < insertedResidue.Count) { Span r = insertedResidue[ir]; if (didOverlap || r.Start < overlap.Value.Start) { insertedThings.Insert(i++, new Thing(r, pos)); ir++; } else { insertedThings.Insert(i++, new Thing(overlap.Value, pos)); didOverlap = true; } } if (!didOverlap) { insertedThings.Insert(i++, new Thing(overlap.Value, pos)); } i--; } NormalizedSpanCollection deletedResidue = NormalizedSpanCollection.Difference(new NormalizedSpanCollection(deleted), new NormalizedSpanCollection(overlap.Value)); if (deletedResidue.Count > 0) { int pos = deletedThings[d].position; deletedThings.RemoveAt(d); bool didOverlap = false; int dr = 0; while (dr < deletedResidue.Count) { Span r = deletedResidue[dr]; if (didOverlap || r.Start < overlap.Value.Start) { deletedThings.Insert(d++, new Thing(r, pos)); dr++; } else { deletedThings.Insert(d++, new Thing(overlap.Value, pos)); didOverlap = true; } } if (!didOverlap) { deletedThings.Insert(d++, new Thing(overlap.Value, pos)); } d--; } } if (inserted.End <= deleted.End) { i++; } if (deleted.End <= inserted.End) { d++; } } while (i < insertedThings.Count && d < deletedThings.Count); } } foreach (KeyValuePair <ITextSnapshot, List <Thing> > pair in buffer2DeletedThings) { foreach (Thing t in pair.Value) { deletedSurrogates[t.position].Add(new SnapshotSpan(pair.Key, t.span)); } } foreach (KeyValuePair <ITextSnapshot, List <Thing> > pair in buffer2InsertedThings) { foreach (Thing t in pair.Value) { insertedSurrogates[t.position].Add(new SnapshotSpan(pair.Key, t.span)); } } }
/// <summary> /// Recursively build span tree. /// </summary> /// <param name="sourceSpan">SnapshotSpan over the source segment covered by this subtree, including both exposed and hidden text.</param> /// <param name="exposedSpans">Set of exposed spans for the entire buffer.</param> /// <param name="lineNumbers">Precomputed line numbers at all seams.</param> /// <param name="slice">The slice of exposed spans in this subtree.</param> /// <returns></returns> private ElisionMapNode Build(SnapshotSpan sourceSpan, NormalizedSpanCollection exposedSpans, int[] lineNumbers, Span slice) { int mid = slice.Start + (slice.Length / 2); Span midExposedSpan = exposedSpans[mid]; Span leftSlice = Span.FromBounds(slice.Start, mid); ElisionMapNode left; Span leftSpan; if (leftSlice.Length > 0) { leftSpan = Span.FromBounds(sourceSpan.Start, midExposedSpan.Start); left = Build(new SnapshotSpan(sourceSpan.Snapshot, leftSpan), exposedSpans, lineNumbers, leftSlice); Debug.Assert(left.TotalSourceSize == leftSpan.Length); } else if (slice.Start == 0 && midExposedSpan.Start != 0) { Debug.Assert(sourceSpan.Start == 0); leftSpan = Span.FromBounds(0, midExposedSpan.Start); // the beginning of the buffer is elided. Do the special case of the first // node in the tree having an exposed size of zero. // TODO: figure this out in advance so we don't screw up the balance of the tree left = new ElisionMapNode(0, leftSpan.Length, 0, TextUtilities.ScanForLineCount(sourceSpan.Snapshot.GetText(leftSpan)), true); } else { leftSpan = new Span(midExposedSpan.Start, 0); left = null; } Span rightSlice = Span.FromBounds(mid + 1, slice.End); ElisionMapNode right; Span rightSpan; if (rightSlice.Length > 0) { rightSpan = Span.FromBounds(exposedSpans[mid + 1].Start, sourceSpan.End); right = Build(new SnapshotSpan(sourceSpan.Snapshot, rightSpan), exposedSpans, lineNumbers, rightSlice); Debug.Assert(right.TotalSourceSize == rightSpan.Length); } else { rightSpan = new Span(sourceSpan.End, 0); right = null; } Span midHiddenSpan = Span.FromBounds(midExposedSpan.End, rightSpan.Start); ITextSnapshot sourceSnapshot = sourceSpan.Snapshot; int startLineNumber = lineNumbers[2 * mid]; int endExposedLineNumber = lineNumbers[2 * mid + 1]; int endSourceLineNumber = lineNumbers[2 * mid + 2]; int exposedLineBreakCount = endExposedLineNumber - startLineNumber; int hiddenLineBreakCount = endSourceLineNumber - endExposedLineNumber; return(new ElisionMapNode(midExposedSpan.Length, sourceSpan.Length - (leftSpan.Length + rightSpan.Length), exposedLineBreakCount, exposedLineBreakCount + hiddenLineBreakCount, left, right, false)); }
private void ReParse() { ParseTree.Builder.Node root = ParseTree.Tree.Root(); ITextSnapshot newSnapshot = buffer.CurrentSnapshot; List <Region> newRegions = new List <Region>(); Stack <char> brackets = new Stack <char>(); Stack <int> offsets = new Stack <int>(); int level = 0; //string text = newSnapshot.GetText(); void Traverse(ParseTree.Builder.Node node) { if (node.name == "'['" || node.name == "'{'" || node.name == "'('") { brackets.Push(node.name[1]); offsets.Push(node.begin); ++level; } if (brackets.Count != 0 && node.name[1] == bracePairs[brackets.Peek()]) { int beginLine = newSnapshot.GetLineFromPosition(offsets.Peek()).LineNumber; int endLine = newSnapshot.GetLineFromPosition(node.begin).LineNumber; if (beginLine != endLine) { if (newRegions.Count != 0 && newRegions.Last().StartOffset == offsets.Peek() + 1 && newRegions.Last().EndOffset == node.begin - 1) { newRegions.Last().Level = level; newRegions.Last().StartLine = beginLine; newRegions.Last().EndLine = endLine; newRegions.Last().StartOffset = offsets.Peek(); newRegions.Last().EndOffset = node.begin; newRegions.Last().Type = brackets.Peek(); } else { newRegions.Add(new Region() { Level = level, StartLine = beginLine, EndLine = endLine, StartOffset = offsets.Peek(), EndOffset = node.begin, Type = brackets.Peek() }); } } --level; offsets.Pop(); brackets.Pop(); } if (node.children == null) { return; } foreach (var child in node.children) { Traverse(child); } } Traverse(root); List <Span> oldSpans = new List <Span>(this.regions.Select(r => AsSnapshotSpan(r, snapshot) .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); 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); } snapshot = newSnapshot; regions = newRegions; if (changeStart <= changeEnd) { TagsChanged?.Invoke(this, new SnapshotSpanEventArgs(new SnapshotSpan(snapshot, Span.FromBounds(changeStart, changeEnd)))); } }
private List<LineSpan> CreateLineSpans(ITextSnapshot textSnapshot, NormalizedSpanCollection allSpans, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); var result = new List<LineSpan>(); foreach (var span in allSpans) { cancellationToken.ThrowIfCancellationRequested(); var lineSpan = GetLineSpan(textSnapshot, span); MergeLineSpans(result, lineSpan); } return result; }
void ReParse() { ITextSnapshot newSnapshot = 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(); string type = string.Empty; int regionStart = -1; int currentTokenIndex = -1; // Support for multiple tokens if (this.GetTextTokenIndex(text, startTokens, out regionStart, out currentTokenIndex)) { type = "S"; // Start token } else if (this.GetTextTokenIndex(text, endTokens, out regionStart, out currentTokenIndex)) { type = "E"; // End token } //else // continue; if (type == "S") { int currentLevel = (currentRegion != null) ? currentRegion.Level : 1; int newLevel; if (!TryGetLevel(text, regionStart, out newLevel)) { 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, LineText = currentRegion.LineText, TokenIndex = currentTokenIndex //currentRegion.TokenIndex }); currentRegion = new PartialRegion() { Level = newLevel, StartLine = line.LineNumber, StartOffset = regionStart, PartialParent = currentRegion.PartialParent, LineText = text, TokenIndex = currentTokenIndex }; } else //this is a new (sub)region { currentRegion = new PartialRegion() { Level = newLevel, StartLine = line.LineNumber, StartOffset = regionStart, PartialParent = currentRegion, LineText = text, TokenIndex = currentTokenIndex }; } } else if (type == "E") { int currentLevel = (currentRegion != null) ? currentRegion.Level : 1; int closingLevel; if (!TryGetLevel(text, regionStart, out closingLevel)) { closingLevel = currentLevel; } //the regions match if (currentRegion != null && currentLevel == closingLevel && currentTokenIndex == currentRegion.TokenIndex) { newRegions.Add(new Region() { Level = currentLevel, StartLine = currentRegion.StartLine, StartOffset = currentRegion.StartOffset, LineText = currentRegion.LineText, TokenIndex = currentRegion.TokenIndex, EndLine = line.LineNumber }); currentRegion = currentRegion.PartialParent; } } } //determine the changed span, and send a changed event with the new spans List <Span> oldSpans = new List <Span>(this.regions.Select(r => AsSnapshotSpan(r, this.snapshot) .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); } this.snapshot = newSnapshot; this.regions = newRegions; if (changeStart <= changeEnd) { ITextSnapshot snap = this.snapshot; if (this.TagsChanged != null) { this.TagsChanged(this, new SnapshotSpanEventArgs(new SnapshotSpan(this.snapshot, Span.FromBounds(changeStart, changeEnd)))); } } }
public async Task <DifferenceViewerPreview?> CreateChangedDocumentPreviewViewAsync(Document oldDocument, Document newDocument, double zoomLevel, CancellationToken cancellationToken) { // CreateNewBufferAsync must be called from the main thread await ThreadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); // Note: We don't use the original buffer that is associated with oldDocument // (and currently open in the editor) for oldBuffer below. This is because oldBuffer // will be used inside a projection buffer inside our inline diff preview below // and platform's implementation currently has a bug where projection buffers // are being leaked. This leak means that if we use the original buffer that is // currently visible in the editor here, the projection buffer span calculation // would be triggered every time user changes some code in this buffer (even though // the diff view would long have been dismissed by the time user edits the code) // resulting in crashes. Instead we create a new buffer from the same content. // TODO: We could use ITextBufferCloneService instead here to clone the original buffer. #pragma warning disable CA2007 // Consider calling ConfigureAwait on the awaited task (containing method uses JTF) var oldBuffer = await CreateNewBufferAsync(oldDocument, cancellationToken); var newBuffer = await CreateNewBufferAsync(newDocument, cancellationToken); #pragma warning restore CA2007 // Consider calling ConfigureAwait on the awaited task // Convert the diffs to be line based. // Compute the diffs between the old text and the new. var diffResult = ComputeEditDifferences(oldDocument, newDocument, cancellationToken); // Need to show the spans in the right that are different. // We also need to show the spans that are in conflict. var originalSpans = GetOriginalSpans(diffResult, cancellationToken); var changedSpans = GetChangedSpans(diffResult, cancellationToken); string?description = null; NormalizedSpanCollection allSpans; if (newDocument.SupportsSyntaxTree) { #pragma warning disable CA2007 // Consider calling ConfigureAwait on the awaited task (containing method uses JTF) var newRoot = await newDocument.GetRequiredSyntaxRootAsync(cancellationToken); #pragma warning restore CA2007 // Consider calling ConfigureAwait on the awaited task var conflictNodes = newRoot.GetAnnotatedNodesAndTokens(ConflictAnnotation.Kind); var conflictSpans = conflictNodes.Select(n => n.Span.ToSpan()).ToList(); var conflictDescriptions = conflictNodes.SelectMany(n => n.GetAnnotations(ConflictAnnotation.Kind)) .Select(a => $"❌ {ConflictAnnotation.GetDescription(a)}") .Distinct(); var warningNodes = newRoot.GetAnnotatedNodesAndTokens(WarningAnnotation.Kind); var warningSpans = warningNodes.Select(n => n.Span.ToSpan()).ToList(); var warningDescriptions = warningNodes.SelectMany(n => n.GetAnnotations(WarningAnnotation.Kind)) .Select(a => $"⚠ {WarningAnnotation.GetDescription(a)}") .Distinct(); var suppressDiagnosticsNodes = newRoot.GetAnnotatedNodesAndTokens(SuppressDiagnosticsAnnotation.Kind); var suppressDiagnosticsSpans = suppressDiagnosticsNodes.Select(n => n.Span.ToSpan()).ToList(); AttachAnnotationsToBuffer(newBuffer, conflictSpans, warningSpans, suppressDiagnosticsSpans); description = conflictSpans.Count == 0 && warningSpans.Count == 0 ? null : string.Join(Environment.NewLine, conflictDescriptions.Concat(warningDescriptions)); allSpans = new NormalizedSpanCollection(conflictSpans.Concat(warningSpans).Concat(changedSpans)); } else { allSpans = new NormalizedSpanCollection(changedSpans); } var originalLineSpans = CreateLineSpans(oldBuffer.CurrentSnapshot, originalSpans, cancellationToken); var changedLineSpans = CreateLineSpans(newBuffer.CurrentSnapshot, allSpans, cancellationToken); if (!originalLineSpans.Any()) { // This means that we have no differences (likely because of conflicts). // In such cases, use the same spans for the left (old) buffer as the right (new) buffer. originalLineSpans = changedLineSpans; } // Create PreviewWorkspaces around the buffers to be displayed on the left and right // so that all IDE services (colorizer, squiggles etc.) light up in these buffers. var leftWorkspace = new PreviewWorkspace(oldDocument.Project.Solution); leftWorkspace.OpenDocument(oldDocument.Id, oldBuffer.AsTextContainer()); var rightWorkspace = new PreviewWorkspace(newDocument.Project.Solution); rightWorkspace.OpenDocument(newDocument.Id, newBuffer.AsTextContainer()); #pragma warning disable CA2007 // Consider calling ConfigureAwait on the awaited task (containing method uses JTF) return(await CreateChangedDocumentViewAsync( oldBuffer, newBuffer, description, originalLineSpans, changedLineSpans, leftWorkspace, rightWorkspace, zoomLevel, cancellationToken)); #pragma warning restore CA2007 // Consider calling ConfigureAwait on the awaited task }
public SearchResults(ITextSnapshot snapshot, NormalizedSpanCollection matches, NormalizedSpanCollection searchedSpans) { this.Snapshot = snapshot; this.Matches = matches; this.SearchedSpans = searchedSpans; }
private void ParseAndCache() { if (!Invalidated) { lock (Timer) Timer.Change(-1, -1); return; } try { Invalidated = false; var newSnapshot = Buffer.CurrentSnapshot; var newRegions = new List <Region>(); PartialRegion currentRegion = null; bool validDsl; var tokens = SyntaxParser.GetExtensions(newSnapshot, out validDsl); if (Invalidated) { return; } int currentLevel = 0; var levelInfo = new List <LevelInfo>(tokens.Length / 16 + 2); LevelInfo lastInfo = null; for (int i = 0; i < tokens.Length; i++) { var t = tokens[i]; if (t.Type == SyntaxType.RuleExtension) { lastInfo = new LevelInfo(); if (currentLevel < levelInfo.Count) { levelInfo[currentLevel] = lastInfo; } else { levelInfo.Add(lastInfo); } currentLevel++; currentRegion = new PartialRegion { Rule = t.Value, Level = currentLevel, StartLine = t.Line - 1, StartOffset = t.Column, PartialParent = currentRegion }; } else if (t.Type == SyntaxType.RuleEnd) { if (currentRegion == null) { continue; } lastInfo.Level--; if (lastInfo.Level >= 0) { continue; } currentLevel--; newRegions.Add(new Region { Rule = t.Value, IsNested = lastInfo.IsNested, Level = currentLevel, StartLine = currentRegion.StartLine, StartOffset = currentRegion.StartOffset, EndLine = t.Line - 1, EndOffset = t.Column - 1 }); lastInfo = currentLevel > 0 ? levelInfo[currentLevel - 1] : null; if (lastInfo != null) { lastInfo.Level--; } currentRegion = currentRegion.PartialParent; } else if (lastInfo != null) { lastInfo.Level++; lastInfo.IsNested = true; } } if (Invalidated) { return; } int changeStart = 0; int changeEnd = newSnapshot.Length; if (!validDsl) { var oldSpans = new Span[Regions.Length]; for (int i = 0; i < Regions.Length; i++) { oldSpans[i] = AsSnapshotSpan(Regions[i], Snapshot).TranslateTo(newSnapshot, SpanTrackingMode.EdgeExclusive).Span; } var newSpans = new Span[newRegions.Count]; for (int i = 0; i < newRegions.Count; i++) { newSpans[i] = AsSnapshotSpan(newRegions[i], newSnapshot).Span; } var oldSpanCollection = new NormalizedSpanCollection(oldSpans); var newSpanCollection = new NormalizedSpanCollection(newSpans); //the changed regions are regions that appear in one set or the other, but not both. var removed = NormalizedSpanCollection.Difference(oldSpanCollection, newSpanCollection); if (removed.Count > 0) { changeStart = removed[0].Start; changeEnd = removed[removed.Count - 1].End; } if (newSpans.Length > 0) { changeStart = Math.Min(changeStart, newSpans[0].Start); changeEnd = Math.Max(changeEnd, newSpans[newSpans.Length - 1].End); } } if (Invalidated) { return; } Snapshot = newSnapshot; Regions = newRegions.ToArray(); if (changeStart <= changeEnd) { TagsChanged(this, new SnapshotSpanEventArgs(new SnapshotSpan(newSnapshot, Span.FromBounds(changeStart, changeEnd)))); } lock (Timer) Timer.Change(1000, -1); } catch { lock (Timer) Timer.Change(5000, -1); } }
void ReParse( ) { m_regionStack.Clear(); ITextSnapshot newSnapshot = 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) { int regionStart = -1; int signStart = -1; string text = line.GetText(); signStart = SimpleNasmLineParser.SkipSpace(text, 0); int currentLevel = (currentRegion != null) ? currentRegion.Level : 0; Region rgn = null; // %if, open a new region if ((regionStart = text.IndexOf(ConditionKeywords[0], StringComparison.InvariantCultureIgnoreCase)) != -1 && regionStart == signStart) { m_regionStack.Push(new Region() { StartLine = line.LineNumber, StartOffset = line.Length }); } // %endif, close current region else if ((regionStart = text.IndexOf(ConditionKeywords[3], StringComparison.InvariantCultureIgnoreCase)) != -1 && regionStart == signStart) { if (m_regionStack.Count == 0) { continue; } rgn = m_regionStack.Pop(); rgn.EndLine = line.LineNumber; newRegions.Add(rgn); } // %elif. %else, close current region and open a new region else if (((regionStart = text.IndexOf(ConditionKeywords[1], StringComparison.InvariantCultureIgnoreCase)) != -1 || (regionStart = text.IndexOf(ConditionKeywords[2], StringComparison.InvariantCultureIgnoreCase)) != -1) && regionStart == signStart) { if (m_regionStack.Count == 0) { continue; } rgn = m_regionStack.Pop(); rgn.EndLine = line.LineNumber - 1; newRegions.Add(rgn); m_regionStack.Push(new Region() { StartLine = line.LineNumber, StartOffset = line.Length }); } } newRegions = newRegions.Where(r => r.EndLine > -1).OrderBy(r => r.StartLine).ToList(); //determine the changed span, and send a changed event with the new spans List <Span> oldSpans = new List <Span>(this.regions.Select(r => AsSnapshotSpan(r, this.snapshot) .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); } this.snapshot = newSnapshot; this.regions = newRegions; if (changeStart <= changeEnd) { ITextSnapshot snap = this.snapshot; if (this.TagsChanged != null) { this.TagsChanged(this, new SnapshotSpanEventArgs( new SnapshotSpan(this.snapshot, Span.FromBounds(changeStart, changeEnd)))); } } }