public void SnapshotSpanSourceCodeReaderTest() { var text = "hello world\r\nHello again!"; var buffer = new MockTextBuffer(text); var snapshot = new MockTextSnapshot(buffer, text); var reader = new SnapshotSpanSourceCodeReader(new SnapshotSpan(snapshot, new Span(0, text.Length))); Assert.AreEqual(reader.Snapshot, snapshot); Assert.AreEqual(reader.Position, 0); var line = reader.ReadLine(); Assert.AreEqual(line, "hello world"); line = reader.ReadLine(); Assert.AreEqual(line, "Hello again!"); reader = new SnapshotSpanSourceCodeReader(new SnapshotSpan(snapshot, new Span(0, text.Length))); int ch = reader.Peek(); Assert.AreEqual(ch, (int)'h'); char[] readBuf = new char[text.Length]; reader.Read(readBuf, 0, readBuf.Length); Assert.AreEqual(new string(readBuf), text); reader = new SnapshotSpanSourceCodeReader(new SnapshotSpan(snapshot, new Span(0, text.Length))); Assert.AreEqual(reader.ReadToEnd(), text); reader.Reset(); Assert.AreEqual(reader.ReadToEnd(), text); reader.Close(); Assert.AreEqual(reader.Snapshot, null); }
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())); } } }