in src/Structure/StructureVisualizer/StructureAdornmentManager.cs [210:316]
private void CreateBlockAdornments(IMappingTagSpan<IBlockTag> tag, NormalizedSnapshotSpanCollection newOrReformattedSpans, double left, double right)
{
NormalizedSnapshotSpanCollection spans = tag.Span.GetSpans(_view.TextSnapshot);
if (spans.Count > 0)
{
//Get the start of the tag's span (which could be out of the view or not even mappable to
//the view's text snapshot).
var statementStart = _view.BufferGraph.MapUpToSnapshot(tag.Tag.StatementStart, PointTrackingMode.Positive, PositionAffinity.Predecessor, _view.TextSnapshot);
if (statementStart.HasValue)
{
var end = _view.BufferGraph.MapUpToSnapshot(tag.Tag.Span.End, PointTrackingMode.Positive, PositionAffinity.Predecessor, _view.TextSnapshot);
if (end.HasValue)
{
//Get the full extent of the block tag so that its adornments will be destroyed if anything in the block changes.
SnapshotSpan extent = new SnapshotSpan(statementStart.Value, end.Value);
bool intersecting = newOrReformattedSpans.IntersectsWith(new NormalizedSnapshotSpanCollection(extent));
if (intersecting)
{
var start = _view.BufferGraph.MapUpToSnapshot(tag.Tag.Span.Start, PointTrackingMode.Positive, PositionAffinity.Predecessor, _view.TextSnapshot);
if (start.HasValue)
{
ITextSnapshotLine startLine = start.Value.GetContainingLine();
if (_showAdornments)
{
double x = -1.0;
foreach (var span in spans)
{
if (span.OverlapsWith(_view.TextViewLines.FormattedSpan))
{
ITextViewLine spanTop = _view.TextViewLines.GetTextViewLineContainingBufferPosition(span.Start);
double yTop = (spanTop == null) ? _view.TextViewLines.FirstVisibleLine.Top : spanTop.Bottom;
ITextViewLine spanBottom = _view.TextViewLines.GetTextViewLineContainingBufferPosition(span.End);
double yBottom = (spanBottom == null) ? _view.TextViewLines.LastVisibleLine.Bottom : spanBottom.Top;
if (yBottom > yTop)
{
if (x < 0.0)
{
//We have a starting point ... but it may be the wrong one. We have three cases to consider:
//1) if (foo) {
// | //Line goes here
//
//2) if (foo)
// {
// | //Line goes here
//
//3) if (bar &&
// foo) {
// | //Line goes here
//
//
//In each of these cases, we need to find the position of the first non-whitespace character on the line
SnapshotPoint blockStart = FindFirstNonwhitespace(startLine);
//If the span start's on the first non-whitespace character of the line, then we have case 2
//(& we're done).
if (blockStart != start.Value)
{
//Case 1 or 3 ... see if the start & statement start are on the same line.
//Is the span start on the same line as the statement start (if so, we have case 1 & are done).
if (!startLine.Extent.Contains(statementStart.Value))
{
//Case 3.
blockStart = statementStart.Value;
}
}
//Get the x-coordinate of the adornment == middle of the character that starts the block.
ITextViewLine tagTop = _view.GetTextViewLineContainingBufferPosition(blockStart);
TextBounds bounds = tagTop.GetCharacterBounds(blockStart);
x = Math.Floor((bounds.Left + bounds.Right) * 0.5); //Make sure this is only a pixel wide.
}
this.CreateBlockAdornment(tag.Tag, extent, x, yTop, yBottom);
}
}
}
}
if (_showMethodSeparator && (tag.Tag.Type == BlockType.Method) && (startLine.End < end.Value))
{
var point = _view.BufferGraph.MapUpToBuffer(end.Value, PointTrackingMode.Negative, PositionAffinity.Predecessor, _view.VisualSnapshot.TextBuffer);
if (point.HasValue)
{
ITextViewLine spanBottom = _view.TextViewLines.GetTextViewLineContainingBufferPosition(end.Value);
if (spanBottom != null)
{
GeometryAdornment adornment = new GeometryAdornment(_coloring.MethodSeparatorAndHighlightColoring.LineBrush,
new RectangleGeometry(new Rect(0.0, 0.0, right - left, 1.0)));
Canvas.SetLeft(adornment, left);
Canvas.SetTop(adornment, spanBottom.Bottom - 1.0);
_methodSeparators.Add(adornment);
_layer.AddAdornment(AdornmentPositioningBehavior.TextRelative, extent, adornment, adornment, OnMethodSeparatorRemoved);
}
}
}
}
}
}
}
}
}