Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove code for arbitrary diagnostic taggers #72409

Merged
merged 10 commits into from
Mar 7, 2024

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -91,9 +91,6 @@ protected sealed override Task ProduceTagsAsync(
private async Task ProduceTagsAsync(
TaggerContext<TTag> context, DocumentSnapshotSpan documentSpanToTag, CancellationToken cancellationToken)
{
if (!_callback.IsEnabled)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no concept of 'enabled' or not. inline diagnostics aren't enabled/disabled depending on if we're using lsp diagnostics or not.

return;

var document = documentSpanToTag.Document;
if (document == null)
return;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System;
using System.Collections.Immutable;
using Microsoft.CodeAnalysis.Editor.Shared.Tagging;
using Microsoft.CodeAnalysis.Editor.Shared.Utilities;
Expand Down Expand Up @@ -63,12 +64,11 @@ SingleDiagnosticKindPullTaggerProvider CreateDiagnosticsTaggerProvider(Diagnosti
protected abstract ImmutableArray<IOption2> Options { get; }
protected virtual ImmutableArray<IOption2> FeatureOptions { get; } = [];

protected abstract bool IsEnabled { get; }

protected abstract bool IncludeDiagnostic(DiagnosticData data);

protected abstract bool TagEquals(TTag tag1, TTag tag2);
protected abstract ITagSpan<TTag>? CreateTagSpan(Workspace workspace, SnapshotSpan span, DiagnosticData data);

protected abstract TTag? CreateTag(Workspace workspace, DiagnosticData diagnostic);

/// <summary>
/// Get the <see cref="DiagnosticDataLocation"/> that should have the tag applied to it.
Expand Down Expand Up @@ -111,4 +111,36 @@ private static ITaggerEventSource CreateEventSourceWorker(ITextBuffer subjectBuf
TaggerEventSources.OnDiagnosticsChanged(subjectBuffer, diagnosticService),
TaggerEventSources.OnTextChanged(subjectBuffer));
}

protected ITagSpan<TTag>? CreateTagSpan(Workspace workspace, SnapshotSpan span, DiagnosticData data)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

moved from intermediary subclass to this type. methods unchanged.

{
var errorTag = CreateTag(workspace, data);
if (errorTag == null)
return null;

// Ensure the diagnostic has at least length 1. Tags must have a non-empty length in order to actually show
// up in the editor.
var adjustedSpan = AdjustSnapshotSpan(span, minimumLength: 1);
if (adjustedSpan.Length == 0)
return null;

return new TagSpan<TTag>(adjustedSpan, errorTag);
}

protected virtual SnapshotSpan AdjustSnapshotSpan(SnapshotSpan span, int minimumLength)
=> AdjustSnapshotSpan(span, minimumLength, maximumLength: int.MaxValue);

protected static SnapshotSpan AdjustSnapshotSpan(SnapshotSpan span, int minimumLength, int maximumLength)
{
var snapshot = span.Snapshot;

// new length
var length = Math.Min(Math.Max(span.Length, minimumLength), maximumLength);

// make sure start + length is smaller than snapshot.Length and start is >= 0
var start = Math.Max(0, Math.Min(span.Start, snapshot.Length - length));

// make sure length is smaller than snapshot.Length which can happen if start == 0
return new SnapshotSpan(snapshot, start, Math.Min(start + length, snapshot.Length) - start);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,124 +3,116 @@
// See the LICENSE file in the project root for more information.

using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.ComponentModel.Composition;
using System.Diagnostics;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.EditAndContinue;
using Microsoft.CodeAnalysis.Editor.Shared.Tagging;
using Microsoft.CodeAnalysis.Editor.Shared.Utilities;
using Microsoft.CodeAnalysis.Editor.Tagging;
using Microsoft.CodeAnalysis.Host.Mef;
using Microsoft.CodeAnalysis.Options;
using Microsoft.CodeAnalysis.Shared.TestHooks;
using Microsoft.CodeAnalysis.Workspaces;
using Microsoft.VisualStudio.Text;
using Microsoft.VisualStudio.Text.Adornments;
using Microsoft.VisualStudio.Text.Classification;
using Microsoft.VisualStudio.Text.Editor;
using Microsoft.VisualStudio.Text.Tagging;
using Microsoft.VisualStudio.Utilities;
using Roslyn.Utilities;

namespace Microsoft.CodeAnalysis.Editor.InlineDiagnostics
namespace Microsoft.CodeAnalysis.Editor.InlineDiagnostics;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

view with whitespace off.


[Export(typeof(ITaggerProvider))]
[ContentType(ContentTypeNames.RoslynContentType)]
[TagType(typeof(InlineDiagnosticsTag))]
[method: ImportingConstructor]
[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)]
internal sealed class InlineDiagnosticsTaggerProvider(
IThreadingContext threadingContext,
IDiagnosticService diagnosticService,
IDiagnosticAnalyzerService analyzerService,
IGlobalOptionService globalOptions,
[Import(AllowDefault = true)] ITextBufferVisibilityTracker? visibilityTracker,
IAsynchronousOperationListenerProvider listenerProvider,
IEditorFormatMapService editorFormatMapService,
IClassificationFormatMapService classificationFormatMapService,
IClassificationTypeRegistryService classificationTypeRegistryService)
: AbstractDiagnosticsTaggerProvider<InlineDiagnosticsTag>(
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

moved to primary constructor. and now derives from AbstractDiagnosticsTaggerProvider. AbstractDiagnosticsAdornmentTaggerProvider was merged into AbstractDiagnosticsTaggerProvider

threadingContext,
diagnosticService,
analyzerService,
globalOptions,
visibilityTracker,
listenerProvider.GetListener(FeatureAttribute.ErrorSquiggles))
{
[Export(typeof(ITaggerProvider))]
[ContentType(ContentTypeNames.RoslynContentType)]
[TagType(typeof(InlineDiagnosticsTag))]
internal sealed class InlineDiagnosticsTaggerProvider : AbstractDiagnosticsAdornmentTaggerProvider<InlineDiagnosticsTag>
{
private readonly IEditorFormatMap _editorFormatMap;
private readonly IClassificationFormatMapService _classificationFormatMapService;
private readonly IClassificationTypeRegistryService _classificationTypeRegistryService;
private readonly IEditorFormatMap _editorFormatMap = editorFormatMapService.GetEditorFormatMap("text");
private readonly IClassificationFormatMapService _classificationFormatMapService = classificationFormatMapService;
private readonly IClassificationTypeRegistryService _classificationTypeRegistryService = classificationTypeRegistryService;

protected sealed override ImmutableArray<IOption2> Options { get; } = [InlineDiagnosticsOptionsStorage.EnableInlineDiagnostics];
protected sealed override ImmutableArray<IOption2> FeatureOptions { get; } = [InlineDiagnosticsOptionsStorage.Location];
protected sealed override ImmutableArray<IOption2> Options { get; } = [InlineDiagnosticsOptionsStorage.EnableInlineDiagnostics];
protected sealed override ImmutableArray<IOption2> FeatureOptions { get; } = [InlineDiagnosticsOptionsStorage.Location];

protected sealed override bool IncludeDiagnostic(DiagnosticData diagnostic)
{
return
diagnostic.Severity is DiagnosticSeverity.Warning or DiagnosticSeverity.Error &&
!string.IsNullOrWhiteSpace(diagnostic.Message) &&
!diagnostic.IsSuppressed;
}

[ImportingConstructor]
[Obsolete(MefConstruction.ImportingConstructorMessage, error: true)]
public InlineDiagnosticsTaggerProvider(
IThreadingContext threadingContext,
IDiagnosticService diagnosticService,
IDiagnosticAnalyzerService analyzerService,
IGlobalOptionService globalOptions,
[Import(AllowDefault = true)] ITextBufferVisibilityTracker? visibilityTracker,
IAsynchronousOperationListenerProvider listenerProvider,
IEditorFormatMapService editorFormatMapService,
IClassificationFormatMapService classificationFormatMapService,
IClassificationTypeRegistryService classificationTypeRegistryService)
: base(threadingContext, diagnosticService, analyzerService, globalOptions, visibilityTracker, listenerProvider)
protected override InlineDiagnosticsTag? CreateTag(Workspace workspace, DiagnosticData diagnostic)
{
Debug.Assert(!string.IsNullOrWhiteSpace(diagnostic.Message));
var errorType = GetErrorTypeFromDiagnostic(diagnostic);
if (errorType is null)
{
_editorFormatMap = editorFormatMapService.GetEditorFormatMap("text");
_classificationFormatMapService = classificationFormatMapService;
_classificationTypeRegistryService = classificationTypeRegistryService;
return null;
}

protected sealed override bool IncludeDiagnostic(DiagnosticData diagnostic)
if (diagnostic.DocumentId is null)
{
return
diagnostic.Severity is DiagnosticSeverity.Warning or DiagnosticSeverity.Error &&
!string.IsNullOrWhiteSpace(diagnostic.Message) &&
!diagnostic.IsSuppressed;
return null;
}

protected override InlineDiagnosticsTag? CreateTag(Workspace workspace, DiagnosticData diagnostic)
var project = workspace.CurrentSolution.GetProject(diagnostic.DocumentId.ProjectId);
if (project is null)
{
Debug.Assert(!string.IsNullOrWhiteSpace(diagnostic.Message));
var errorType = GetErrorTypeFromDiagnostic(diagnostic);
if (errorType is null)
{
return null;
}

if (diagnostic.DocumentId is null)
{
return null;
}
return null;
}

var project = workspace.CurrentSolution.GetProject(diagnostic.DocumentId.ProjectId);
if (project is null)
{
return null;
}
var locationOption = GlobalOptions.GetOption(InlineDiagnosticsOptionsStorage.Location, project.Language);
var navigateService = workspace.Services.GetRequiredService<INavigateToLinkService>();
return new InlineDiagnosticsTag(errorType, diagnostic, _editorFormatMap, _classificationFormatMapService,
_classificationTypeRegistryService, locationOption, navigateService);
}

var locationOption = GlobalOptions.GetOption(InlineDiagnosticsOptionsStorage.Location, project.Language);
var navigateService = workspace.Services.GetRequiredService<INavigateToLinkService>();
return new InlineDiagnosticsTag(errorType, diagnostic, _editorFormatMap, _classificationFormatMapService,
_classificationTypeRegistryService, locationOption, navigateService);
private static string? GetErrorTypeFromDiagnostic(DiagnosticData diagnostic)
{
if (diagnostic.Severity == DiagnosticSeverity.Error)
{
return diagnostic.CustomTags.Contains(WellKnownDiagnosticTags.EditAndContinue)
? EditAndContinueErrorTypeDefinition.Name
: PredefinedErrorTypeNames.SyntaxError;
}

private static string? GetErrorTypeFromDiagnostic(DiagnosticData diagnostic)
else if (diagnostic.Severity == DiagnosticSeverity.Warning)
{
if (diagnostic.Severity == DiagnosticSeverity.Error)
{
return diagnostic.CustomTags.Contains(WellKnownDiagnosticTags.EditAndContinue)
? EditAndContinueErrorTypeDefinition.Name
: PredefinedErrorTypeNames.SyntaxError;
}
else if (diagnostic.Severity == DiagnosticSeverity.Warning)
{
return PredefinedErrorTypeNames.Warning;
}
else
{
return null;
}
return PredefinedErrorTypeNames.Warning;
}
else
{
return null;
}

/// <summary>
/// TODO: is there anything we can do better here? Inline diagnostic tags are not really data, but more UI
/// elements with specific controls, positions and events attached to them. There doesn't seem to be a safe way
/// to reuse any of these currently. Ideally we could do something similar to inline-hints where there's a data
/// tagger portion (which is async and has clean equality semantics), and then the UI portion which just
/// translates those data-tags to the UI tags.
/// <para>
/// Doing direct equality means we'll always end up regenerating all tags. But hopefully there won't be that
/// many in a document to matter.
/// </para>
/// </summary>
protected sealed override bool TagEquals(InlineDiagnosticsTag tag1, InlineDiagnosticsTag tag2)
=> tag1 == tag2;
}

/// <summary>
/// TODO: is there anything we can do better here? Inline diagnostic tags are not really data, but more UI
/// elements with specific controls, positions and events attached to them. There doesn't seem to be a safe way
/// to reuse any of these currently. Ideally we could do something similar to inline-hints where there's a data
/// tagger portion (which is async and has clean equality semantics), and then the UI portion which just
/// translates those data-tags to the UI tags.
/// <para>
/// Doing direct equality means we'll always end up regenerating all tags. But hopefully there won't be that
/// many in a document to matter.
/// </para>
/// </summary>
protected sealed override bool TagEquals(InlineDiagnosticsTag tag1, InlineDiagnosticsTag tag2)
=> tag1 == tag2;
}

This file was deleted.

Loading
Loading