private void ReplaceProjectionSpans(ReadOnlyCollection <SnapshotSpan> oldProjectionSpans, List <SpanRangeEdit> spanEdits) { Debug.Assert(spanEdits.Count > 0); int start = spanEdits[0].Start; int end = spanEdits[spanEdits.Count - 1].End; var replacement = new List <object>(); replacement.AddRange(spanEdits[0].Replacement); int lastEnd = spanEdits[0].End; for (int i = 1; i < spanEdits.Count; i++) { SpanRangeEdit edit = spanEdits[i]; int gap = edit.Start - lastEnd; // there is always at least prompt span in between subsequent edits Debug.Assert(gap != 0); // spans can't share more then one span Debug.Assert(gap >= -1); if (gap == -1) { replacement.AddRange(edit.Replacement.Skip(1)); } else { replacement.AddRange(oldProjectionSpans.Skip(lastEnd).Take(gap).Select(CreateTrackingSpan)); replacement.AddRange(edit.Replacement); } lastEnd = edit.End; } _projectionBuffer.ReplaceSpans(start, end - start, replacement, EditOptions.None, s_suppressPromptInjectionTag); }
private void UpdateTemplateSpans(TextReader reader, int startSpan = 0, int offset = 0) { var tokenizer = new TemplateTokenizer(reader); TemplateToken? token; List <object> newProjectionSpans = new List <object>(); // spans in the projection buffer List <SpanInfo> newSpanInfos = new List <SpanInfo>(); // our SpanInfo's, with spans in the on-disk buffer while ((token = tokenizer.GetNextToken()) != null) { var curToken = token.Value; var sourceSpan = Span.FromBounds(curToken.Start + offset, curToken.End + offset + 1); switch (curToken.Kind) { case TemplateTokenKind.Block: case TemplateTokenKind.Comment: case TemplateTokenKind.Variable: // template spans are setup to not grow. We'll track the edits and update their // text if something causes them to grow. if (newSpanInfos.Count == 0 && startSpan == 0) { // insert a zero-length span which will grow if the user inserts before the template tag var emptySpan = new CustomTrackingSpan( _diskBuffer.CurrentSnapshot, new Span(0, 0), PointTrackingMode.Negative, PointTrackingMode.Positive ); newProjectionSpans.Add(emptySpan); newSpanInfos.Add(new SpanInfo(emptySpan, TemplateTokenKind.Text)); } newSpanInfos.Add( new SpanInfo( new CustomTrackingSpan( _diskBuffer.CurrentSnapshot, sourceSpan, PointTrackingMode.Positive, PointTrackingMode.Negative ), curToken.Kind, curToken.Kind == TemplateTokenKind.Block ? DjangoBlock.Parse(_diskBuffer.CurrentSnapshot.GetText(sourceSpan)) : null ) ); newProjectionSpans.Add( new CustomTrackingSpan( _templateBuffer.CurrentSnapshot, sourceSpan, PointTrackingMode.Positive, PointTrackingMode.Negative ) ); break; case TemplateTokenKind.Text: var htmlSpan = _htmlBuffer.CurrentSnapshot.Version.CreateCustomTrackingSpan( sourceSpan, TrackingFidelityMode.Forward, new LanguageSpanCustomState(sourceSpan.Start, sourceSpan.End), TrackToVersion ); var diskSpan = _diskBuffer.CurrentSnapshot.Version.CreateCustomTrackingSpan( sourceSpan, TrackingFidelityMode.Forward, new LanguageSpanCustomState(sourceSpan.Start, sourceSpan.End), TrackToVersion ); newProjectionSpans.Add(htmlSpan); newSpanInfos.Add(new SpanInfo(diskSpan, TemplateTokenKind.Text)); break; } } if (newSpanInfos.Count == 0 || newSpanInfos[newSpanInfos.Count - 1].Kind != TemplateTokenKind.Text) { // insert an empty span at the end which will receive new text. var emptySpan = _diskBuffer.CurrentSnapshot.Version.CreateCustomTrackingSpan( new Span(_diskBuffer.CurrentSnapshot.Length, 0), TrackingFidelityMode.Forward, new LanguageSpanCustomState(_diskBuffer.CurrentSnapshot.Length, _diskBuffer.CurrentSnapshot.Length), TrackToVersion ); newProjectionSpans.Add(emptySpan); newSpanInfos.Add(new SpanInfo(emptySpan, TemplateTokenKind.Text)); } var oldSpanCount = _spans.Count; _spans.RemoveRange(startSpan, oldSpanCount - startSpan); _spans.AddRange(newSpanInfos); _projBuffer.ReplaceSpans(startSpan, oldSpanCount - startSpan, newProjectionSpans, _editOptions, null); ProjectionClassifier classifier; if (newSpanInfos.Count > 0 && _projBuffer.Properties.TryGetProperty <ProjectionClassifier>(typeof(ProjectionClassifier), out classifier)) { classifier.RaiseClassificationChanged( newSpanInfos[0].DiskBufferSpan.GetStartPoint(_diskBuffer.CurrentSnapshot), newSpanInfos[newSpanInfos.Count - 1].DiskBufferSpan.GetEndPoint(_diskBuffer.CurrentSnapshot) ); } }
private void HandleContentTypeTagsChanged() { ITextSnapshot snapshot = DiskBuffer.CurrentSnapshot; ITextSnapshot htmlSnapshot = _htmlBuffer.CurrentSnapshot; ITextSnapshot phpSnapshot = TemplateBuffer.CurrentSnapshot; SnapshotSpan completeSnapshot = new SnapshotSpan(snapshot, 0, snapshot.Length); IMappingTagSpan <ContentTypeTag>[] tags = _contentTypeTagger.GetTags(completeSnapshot).ToArray(); IMappingPoint[] trackingPoints = new IMappingPoint[tags.Length]; for (int i = 0; i < tags.Length; i++) { IMappingTagSpan <ContentTypeTag> tag = tags[i]; switch (tag.Tag.RegionType) { case RegionType.Begin: trackingPoints[i] = tag.Span.Start; break; case RegionType.End: trackingPoints[i] = tag.Span.End; break; default: throw new NotSupportedException(); } } List <ITrackingSpan> projectionSpans = new List <ITrackingSpan>(); List <SpanInfo> spans = new List <SpanInfo>(); for (int i = 0; i < tags.Length; i++) { IMappingTagSpan <ContentTypeTag> tag = tags[i]; Span sourceSpan; if (i == 0) { sourceSpan = new Span(0, trackingPoints[0].GetPoint(snapshot, PositionAffinity.Successor) ?? 0); } else { SnapshotPoint?startPoint = trackingPoints[i - 1].GetPoint(snapshot, PositionAffinity.Successor); SnapshotPoint?endPoint = trackingPoints[i].GetPoint(snapshot, PositionAffinity.Successor); if (!startPoint.HasValue || !endPoint.HasValue) { throw new InvalidOperationException(); } sourceSpan = Span.FromBounds(startPoint.Value.Position, endPoint.Value.Position); } switch (tag.Tag.RegionType) { case RegionType.Begin: { // handle the Text region that ended where this tag started ITrackingSpan projectionSpan = htmlSnapshot.Version.CreateCustomTrackingSpan( sourceSpan, TrackingFidelityMode.Forward, new LanguageSpanCustomState(sourceSpan), TrackToVersion); ITrackingSpan diskBufferSpan = snapshot.Version.CreateCustomTrackingSpan( sourceSpan, TrackingFidelityMode.Forward, new LanguageSpanCustomState(sourceSpan), TrackToVersion); projectionSpans.Add(projectionSpan); spans.Add(new SpanInfo(diskBufferSpan, TemplateTokenKind.Text)); break; } case RegionType.End: { // handle the code region that ended where this tag ended ITrackingSpan projectionSpan = new CustomTrackingSpan(phpSnapshot.CreateTrackingSpan(sourceSpan, SpanTrackingMode.EdgeExclusive)); ITrackingSpan diskBufferSpan = new CustomTrackingSpan(snapshot.CreateTrackingSpan(sourceSpan, SpanTrackingMode.EdgeExclusive)); projectionSpans.Add(projectionSpan); spans.Add(new SpanInfo(diskBufferSpan, TemplateTokenKind.Block)); break; } default: throw new NotSupportedException(); } } if (true) { int startPosition = 0; if (tags.Length > 0) { SnapshotPoint?startPoint = trackingPoints.Last().GetPoint(snapshot, PositionAffinity.Successor); if (!startPoint.HasValue) { throw new InvalidOperationException(); } startPosition = startPoint.Value.Position; } Span sourceSpan = Span.FromBounds(startPosition, snapshot.Length); RegionType finalRegionType = tags.Length > 0 ? tags.Last().Tag.RegionType : RegionType.End; switch (finalRegionType) { case RegionType.Begin: { // handle the code region that ended at the end of the document ITrackingSpan projectionSpan = new CustomTrackingSpan(phpSnapshot.CreateTrackingSpan(sourceSpan, SpanTrackingMode.EdgePositive)); ITrackingSpan diskBufferSpan = new CustomTrackingSpan(snapshot.CreateTrackingSpan(sourceSpan, SpanTrackingMode.EdgePositive)); projectionSpans.Add(projectionSpan); spans.Add(new SpanInfo(diskBufferSpan, TemplateTokenKind.Block)); break; } case RegionType.End: { // handle the Text region that ended at the end of the document ITrackingSpan projectionSpan = htmlSnapshot.Version.CreateCustomTrackingSpan( sourceSpan, TrackingFidelityMode.Forward, new LanguageSpanCustomState(sourceSpan), TrackToVersion); ITrackingSpan diskBufferSpan = snapshot.Version.CreateCustomTrackingSpan( sourceSpan, TrackingFidelityMode.Forward, new LanguageSpanCustomState(sourceSpan), TrackToVersion); projectionSpans.Add(projectionSpan); spans.Add(new SpanInfo(diskBufferSpan, TemplateTokenKind.Text)); break; } default: throw new NotSupportedException(); } } int startSpan = 0; int oldSpanCount = _spans.Count; _spans.RemoveRange(startSpan, oldSpanCount - startSpan); _spans.AddRange(spans); _projBuffer.ReplaceSpans(startSpan, oldSpanCount - startSpan, projectionSpans.ToArray(), _editOptions, null); ProjectionClassifier classifier; if (spans.Count > 0 && _projBuffer.Properties.TryGetProperty <ProjectionClassifier>(typeof(ProjectionClassifier), out classifier)) { classifier.RaiseClassificationChanged( spans[0].DiskBufferSpan.GetStartPoint(_diskBuffer.CurrentSnapshot), spans[spans.Count - 1].DiskBufferSpan.GetEndPoint(_diskBuffer.CurrentSnapshot) ); } }