-
Notifications
You must be signed in to change notification settings - Fork 4k
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
Changes from 8 commits
18cb45b
0eb10ba
ea66645
1f4ca1c
0afab5b
d94e083
05d1290
63a00ea
8bfe2d0
4c6384c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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; | ||
|
@@ -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. | ||
|
@@ -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) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 |
---|---|---|
|
@@ -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; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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>( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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.
There was a problem hiding this comment.
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.