protected void ClassifyTemplateBody(ITextSnapshot snapshot, List <ClassificationSpan> spans, TemplateRegion region, int prefixLength, int suffixLength) { switch (region.Kind) { case TemplateTokenKind.Comment: spans.Add( new ClassificationSpan( new SnapshotSpan( snapshot, new Span(region.Start + prefixLength, region.Text.Length - (prefixLength + suffixLength)) ), _classifierProvider._commentClassType ) ); break; case TemplateTokenKind.Variable: var filterInfo = DjangoVariable.Parse(region.Text); if (filterInfo != null) { foreach (var curSpan in filterInfo.GetSpans()) { spans.Add(ToClassificationSpan(curSpan, snapshot, region.Start)); } } break; case TemplateTokenKind.Block: var blockInfo = region.Block ?? DjangoBlock.Parse(region.Text); if (blockInfo != null) { foreach (var curSpan in blockInfo.GetSpans()) { spans.Add(ToClassificationSpan(curSpan, snapshot, region.Start)); } } else if (region.Text.Length > (prefixLength + suffixLength)) // unterminated block at end of file { spans.Add( new ClassificationSpan( new SnapshotSpan( snapshot, new Span(region.Start + prefixLength, region.Text.Length - (prefixLength + suffixLength)) ), _classifierProvider._classType ) ); } break; } }
protected override void Reparse(string text) { Block = DjangoBlock.Parse(text, trim: true); }
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) ); } }
public void DiskBufferChanged(object sender, TextContentChangedEventArgs e) { foreach (var change in e.Changes) { int closest; if (TryGetSingleStartSpan(e, change, out closest)) { var closestSpan = _spans[closest]; int recalcPosition = -1; bool recalc = false; if (closestSpan.Kind == TemplateTokenKind.Text) { // we're in a section of HTML or whatever language the actual template is being applied to. // First, check and see if the user has just pasted some code which includes a template, // in which case we'll recalculate everything. if (!(recalc = ContainsTemplateMarkup(change))) { // Then see if they inserted/deleted text creates a template tag by being merged // with the text around us. if (change.NewSpan.Start != 0 && change.NewSpan.End < e.After.Length) { // Check if the character before us plus the inserted character(s) makes a template tag var newText = e.After.GetText(new Span(change.NewSpan.Start - 1, 2)); if (Array.IndexOf(_templateTags, newText) != -1) { if (closest != 0) { recalcPosition = _spans[--closest].DiskBufferSpan.GetStartPoint(e.After); } else { recalcPosition = _spans[closest].DiskBufferSpan.GetStartPoint(e.After); } recalc = true; } } if (!recalc && change.NewSpan.End >= 2) { // check if we inserted template tags at the end var newText = e.After.GetText(new Span(change.NewSpan.End - 2, 2)); if (Array.IndexOf(_templateTags, newText) != -1) { if (closest != 0) { recalcPosition = _spans[--closest].DiskBufferSpan.GetStartPoint(e.After); } else { recalcPosition = _spans[closest].DiskBufferSpan.GetStartPoint(e.After); } recalc = true; } } if (!recalc && change.NewSpan.Start + 2 <= e.After.Length) { // check if the inserted char plus the char after us makes a template tag var newText = e.After.GetText(new Span(change.NewSpan.Start, 2)); if (Array.IndexOf(_templateTags, newText) != -1) { if (closest != 0) { recalcPosition = _spans[--closest].DiskBufferSpan.GetStartPoint(e.After); } else { recalcPosition = _spans[closest].DiskBufferSpan.GetStartPoint(e.After); } recalc = true; } } } if (!recalc && change.NewSpan.Start == closestSpan.DiskBufferSpan.GetStartPoint(e.After) && change.NewSpan.Length == 0) { // finally, if we are just deleting code (change.NewSpan.Length == 0 indicates there's no insertions) from // the start of the buffer then we are definitely deleting a } which means we need to re-calc. We know // we're deleting the } because it's the last char in the buffer before us because it has // to be the end of a template. closestSpan = _spans[0]; closest = 0; recalc = true; } } else { // check if the newly inserted text makes a tag, we include the character before // our span and the character after. Span changeBounds = change.NewSpan; if (changeBounds.Start > 0) { changeBounds = new Span(changeBounds.Start - 1, changeBounds.Length + 1); } if (changeBounds.End < e.After.Length) { changeBounds = new Span(changeBounds.Start, changeBounds.Length + 1); } var newText = e.After.GetText(changeBounds); foreach (var tag in _templateTags) { if (newText.Contains(tag)) { recalc = true; break; } } if (!recalc) { var templateStart = closestSpan.DiskBufferSpan.GetStartPoint(e.After); if (change.NewSpan.Start <= templateStart + 1 || change.NewSpan.End == closestSpan.DiskBufferSpan.GetEndPoint(e.After) - 1) { // we are altering one of the 1st two characters or the last character. // Because we're a template that could be messing us up. // We recalcuate from the previous text buffer // because the act of deleting this character could have turned us into a text // buffer, and we need to replace and extend the previous text buffer. recalcPosition = _spans[--closest].DiskBufferSpan.GetStartPoint(e.After); recalc = true; } } } if (recalc) { var start = recalcPosition != -1 ? recalcPosition : closestSpan.DiskBufferSpan.GetStartPoint(e.After).Position; var reader = new SnapshotSpanSourceCodeReader(new SnapshotSpan(e.After, Span.FromBounds(start, e.After.Length))); UpdateTemplateSpans(reader, closest, start); } else if (closestSpan.Kind == TemplateTokenKind.Block) { // re-parse the block _spans[closest] = new SpanInfo( closestSpan.DiskBufferSpan, closestSpan.Kind, DjangoBlock.Parse(closestSpan.DiskBufferSpan.GetText(closestSpan.DiskBufferSpan.TextBuffer.CurrentSnapshot)) ); } } else { UpdateTemplateSpans(new StringReader(e.After.GetText())); } } }