Example #1
0
        /// <summary>
        /// Iterates through the mapping of line number to span and draws the diagnostic in the appropriate position on the screen,
        /// as well as adding the tag to the adornment layer.
        /// </summary>
        protected override void AddAdornmentsToAdornmentLayer_CallOnlyOnUIThread(NormalizedSnapshotSpanCollection changedSpanCollection)
        {
            // this method should only run on UI thread as we do WPF here.
            Contract.ThrowIfFalse(TextView.VisualElement.Dispatcher.CheckAccess());
            if (changedSpanCollection.IsEmpty())
            {
                return;
            }

            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;
                }

                var geometry = viewLines.GetMarkerGeometry(span);
                if (geometry is null)
                {
                    continue;
                }

                // Need to get the SnapshotPoint to be able to get the IWpfTextViewLine
                var point = tagMappingSpan.Span.Start.GetPoint(TextView.TextSnapshot, PositionAffinity.Predecessor);
                if (point == null)
                {
                    continue;
                }

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

                if (lineView is null)
                {
                    continue;
                }

                // 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())
                {
                    continue;
                }

                var tag = tagMappingSpan.Tag;
                var classificationType = _classificationRegistryService.GetClassificationType(InlineDiagnosticsTag.GetClassificationId(tag.ErrorType));
                var graphicsResult     = tag.GetGraphics(TextView, geometry, 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)
                {
                    continue;
                }

                Canvas.SetLeft(visualElement,
                               tag.Location == InlineDiagnosticsLocations.PlacedAtEndOfCode ? lineView.Right :
                               tag.Location == InlineDiagnosticsLocations.PlacedAtEndOfEditor ? TextView.ViewportRight - visualElement.DesiredSize.Width :
                               throw ExceptionUtilities.UnexpectedValue(tag.Location));

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

                AdornmentLayer.AddAdornment(
                    behavior: AdornmentPositioningBehavior.TextRelative,
                    visualSpan: lineView.Extent,
                    tag: tag,
                    adornment: visualElement,
                    removedCallback: delegate { graphicsResult.Dispose(); });
            }
        }
        /// <summary>
        /// Iterates through the mapping of line number to span and draws the diagnostic in the appropriate position on the screen,
        /// as well as adding the tag to the adornment layer.
        /// </summary>
        protected override void AddAdornmentsToAdornmentLayer_CallOnlyOnUIThread(NormalizedSnapshotSpanCollection changedSpanCollection)
        {
            // this method should only run on UI thread as we do WPF here.
            Contract.ThrowIfFalse(TextView.VisualElement.Dispatcher.CheckAccess());
            if (changedSpanCollection.IsEmpty())
            {
                return;
            }

            var viewLines = TextView.TextViewLines;

            using var _ = PooledDictionary <IWpfTextViewLine, IMappingTagSpan <InlineDiagnosticsTag> > .GetInstance(out var map);

            // 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;
                }

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

                    var viewLine = viewLines.GetTextViewLineContainingBufferPosition(mappedPoint);

                    // 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())
                {
                    continue;
                }

                var tag = tagMappingSpan.Tag;
                var classificationType = _classificationRegistryService.GetClassificationType(InlineDiagnosticsTag.GetClassificationId(tag.ErrorType));

                // 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)
                {
                    graphicsResult.Dispose();
                    continue;
                }

                Canvas.SetLeft(visualElement,
                               tag.Location == InlineDiagnosticsLocations.PlacedAtEndOfCode ? lineView.Right :
                               tag.Location == InlineDiagnosticsLocations.PlacedAtEndOfEditor ? TextView.ViewportRight - visualElement.DesiredSize.Width :
                               throw ExceptionUtilities.UnexpectedValue(tag.Location));

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

                AdornmentLayer.AddAdornment(
                    behavior: AdornmentPositioningBehavior.TextRelative,
                    visualSpan: lineView.Extent,
                    tag: tag,
                    adornment: visualElement,
                    removedCallback: delegate { graphicsResult.Dispose(); });
            }
        }