Skip to content

Commit

Permalink
Merge pull request #60468 from akhera99/bugs/inline_diagnostics_switc…
Browse files Browse the repository at this point in the history
…h_enum

Inline Diagnostics: Fix conflicting diagnostics and general code cleanup
  • Loading branch information
akhera99 authored Mar 30, 2022
2 parents 88d8b63 + 3d37248 commit 91bd6e5
Show file tree
Hide file tree
Showing 3 changed files with 37 additions and 67 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -284,7 +284,7 @@ protected bool ShouldDrawTag(SnapshotSpan snapshotSpan, IMappingTagSpan<T> mappi
}

var mappedPoint = TextView.BufferGraph.MapUpToSnapshot(
point.Value, PointTrackingMode.Negative, PositionAffinity.Predecessor, TextView.VisualSnapshot);
point.Value, PointTrackingMode.Negative, PositionAffinity.Predecessor, TextView.TextSnapshot);
if (mappedPoint == null)
{
return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -130,41 +130,49 @@ protected override void AddAdornmentsToAdornmentLayer_CallOnlyOnUIThread(Normali
}

var viewLines = TextView.TextViewLines;
using var _ = PooledDictionary<int, IMappingTagSpan<InlineDiagnosticsTag>>.GetInstance(out var map);
AddSpansOnEachLine(changedSpanCollection, map);
foreach (var (lineNum, tagMappingSpan) in map)
{
// Mapping the IMappingTagSpan back up to the TextView's visual snapshot to ensure there will
// be no adornments drawn on disjoint spans.
if (!TryMapToSingleSnapshotSpan(tagMappingSpan.Span, TextView.TextSnapshot, out var span))
{
continue;
}
using var _ = PooledDictionary<IWpfTextViewLine, IMappingTagSpan<InlineDiagnosticsTag>>.GetInstance(out var map);

var geometry = viewLines.GetMarkerGeometry(span);
if (geometry is null)
// First loop iterates through the snap collection and determines if an inline diagnostic can be drawn.
// Creates a mapping of the view line to the IMappingTagSpan with getting the first error that appears
// on the line if there are multiple.
foreach (var changedSpan in changedSpanCollection)
{
if (!viewLines.IntersectsBufferSpan(changedSpan))
{
continue;
}

// Need to get the SnapshotPoint to be able to get the IWpfTextViewLine
var point = tagMappingSpan.Span.End.GetPoint(TextView.TextSnapshot, PositionAffinity.Predecessor);
if (point == null)
var tagSpans = TagAggregator.GetTags(changedSpan);
foreach (var tagMappingSpan in tagSpans)
{
continue;
}
if (!ShouldDrawTag(changedSpan, tagMappingSpan, out var mappedPoint))
{
continue;
}

var lineView = viewLines.GetTextViewLineContainingBufferPosition(point.Value);
var viewLine = viewLines.GetTextViewLineContainingBufferPosition(mappedPoint);

if (lineView is null)
{
continue;
// If the line does not have an associated tagMappingSpan and changedSpan, then add the first one.
if (!map.TryGetValue(viewLine, out var value))
{
map.Add(viewLine, tagMappingSpan);
}
else if (value.Tag.ErrorType is not PredefinedErrorTypeNames.SyntaxError && tagMappingSpan.Tag.ErrorType is PredefinedErrorTypeNames.SyntaxError)
{
// Draw the first instance of an error, if what is stored in the map at a specific line is
// not an error, then replace it. Otherwise, just get the first warning on the line.
map[viewLine] = tagMappingSpan;
}
}
}

// Second loop iterates through the map to go through and create the graphics that is being drawn
// on the canvas as well adding the tag to the Inline Diagnostics adornment layer.
foreach (var (lineView, tagMappingSpan) in map)
{
// Looking for IEndOfLineTags and seeing if they exist on the same line as where the
// diagnostic would be drawn. If they are the same, then we do not want to draw
// the diagnostic.

var obstructingTags = _endLineTagAggregator.GetTags(lineView.Extent);
if (obstructingTags.Where(tag => tag.Tag.Type is not "Inline Diagnostics").Any())
{
Expand All @@ -173,9 +181,12 @@ protected override void AddAdornmentsToAdornmentLayer_CallOnlyOnUIThread(Normali

var tag = tagMappingSpan.Tag;
var classificationType = _classificationRegistryService.GetClassificationType(InlineDiagnosticsTag.GetClassificationId(tag.ErrorType));
var graphicsResult = tag.GetGraphics(TextView, geometry, GetFormat(classificationType));

// Pass in null! because the geometry is unused for drawing anything for Inline Diagnostics
var graphicsResult = tag.GetGraphics(TextView, unused: null!, GetFormat(classificationType));

var visualElement = graphicsResult.VisualElement;

// Only place the diagnostics if the diagnostic would not intersect with the editor window
if (lineView.Right >= TextView.ViewportWidth - visualElement.DesiredSize.Width)
{
Expand All @@ -188,7 +199,7 @@ protected override void AddAdornmentsToAdornmentLayer_CallOnlyOnUIThread(Normali
tag.Location == InlineDiagnosticsLocations.PlacedAtEndOfEditor ? TextView.ViewportRight - visualElement.DesiredSize.Width :
throw ExceptionUtilities.UnexpectedValue(tag.Location));

Canvas.SetTop(visualElement, geometry.Bounds.Bottom - visualElement.DesiredSize.Height);
Canvas.SetTop(visualElement, lineView.Bottom - visualElement.DesiredSize.Height);

AdornmentLayer.AddAdornment(
behavior: AdornmentPositioningBehavior.TextRelative,
Expand All @@ -198,46 +209,5 @@ protected override void AddAdornmentsToAdornmentLayer_CallOnlyOnUIThread(Normali
removedCallback: delegate { graphicsResult.Dispose(); });
}
}

/// <summary>
/// Get the spans located on each line so that it can only display the first one that appears on the line
/// </summary>
private void AddSpansOnEachLine(NormalizedSnapshotSpanCollection changedSpanCollection,
Dictionary<int, IMappingTagSpan<InlineDiagnosticsTag>> map)
{
var viewLines = TextView.TextViewLines;

foreach (var changedSpan in changedSpanCollection)
{
if (!viewLines.IntersectsBufferSpan(changedSpan))
{
continue;
}

var tagSpans = TagAggregator.GetTags(changedSpan);
foreach (var tagMappingSpan in tagSpans)
{
if (!ShouldDrawTag(changedSpan, tagMappingSpan, out var mappedPoint))
{
continue;
}

// mappedPoint is known to not be null here because it is checked in the ShouldDrawTag method call.
var lineNum = mappedPoint.GetContainingLine().LineNumber;

// If the line does not have an associated tagMappingSpan and changedSpan, then add the first one.
if (!map.TryGetValue(lineNum, out var value))
{
map.Add(lineNum, tagMappingSpan);
}
else if (value.Tag.ErrorType is not PredefinedErrorTypeNames.SyntaxError && tagMappingSpan.Tag.ErrorType is PredefinedErrorTypeNames.SyntaxError)
{
// Draw the first instance of an error, if what is stored in the map at a specific line is
// not an error, then replace it. Otherwise, just get the first warning on the line.
map[lineNum] = tagMappingSpan;
}
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ public InlineDiagnosticsTag(string errorType, DiagnosticData diagnostic, IEditor
/// <summary>
/// Creates a GraphicsResult object which is the error block based on the geometry and formatting set for the item.
/// </summary>
public override GraphicsResult GetGraphics(IWpfTextView view, Geometry bounds, TextFormattingRunProperties format)
public override GraphicsResult GetGraphics(IWpfTextView view, Geometry unused, TextFormattingRunProperties format)
{
var block = new TextBlock
{
Expand Down

0 comments on commit 91bd6e5

Please sign in to comment.