diff --git a/src/EditorFeatures/CSharpTest/InlineDiagnostics/InlineDiagnosticsTaggerProviderTests.cs b/src/EditorFeatures/CSharpTest/InlineDiagnostics/InlineDiagnosticsTaggerProviderTests.cs index 65376ce550b0a..d53aef0d65d69 100644 --- a/src/EditorFeatures/CSharpTest/InlineDiagnostics/InlineDiagnosticsTaggerProviderTests.cs +++ b/src/EditorFeatures/CSharpTest/InlineDiagnostics/InlineDiagnosticsTaggerProviderTests.cs @@ -2,69 +2,63 @@ // 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 System.Threading.Tasks; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Editor.InlineDiagnostics; -using Microsoft.CodeAnalysis.Editor.UnitTests.CodeActions; -using Microsoft.CodeAnalysis.Editor.UnitTests.Extensions; using Microsoft.CodeAnalysis.Editor.UnitTests.Squiggles; -using Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces; using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Test.Utilities; using Microsoft.VisualStudio.Text.Adornments; using Microsoft.VisualStudio.Text.Tagging; using Roslyn.Test.Utilities; -using Roslyn.Utilities; using Xunit; -namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.InlineDiagnostics +namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.InlineDiagnostics; + +[UseExportProvider] +[Trait(Traits.Feature, Traits.Features.ErrorSquiggles), Trait(Traits.Feature, Traits.Features.Tagging)] +public class InlineDiagnosticsTaggerProviderTests { - [UseExportProvider] - [Trait(Traits.Feature, Traits.Features.ErrorSquiggles), Trait(Traits.Feature, Traits.Features.Tagging)] - public class InlineDiagnosticsTaggerProviderTests + [WpfFact] + public async Task ErrorTagGeneratedForError() { - [WpfFact] - public async Task ErrorTagGeneratedForError() - { - var spans = await GetTagSpansAsync("class C {"); - var firstSpan = Assert.Single(spans); - Assert.Equal(PredefinedErrorTypeNames.SyntaxError, firstSpan.Tag.ErrorType); - } + var spans = await GetTagSpansAsync("class C {"); + var firstSpan = Assert.Single(spans); + Assert.Equal(PredefinedErrorTypeNames.SyntaxError, firstSpan.Tag.ErrorType); + } - [WpfFact] - public async Task ErrorTagGeneratedForErrorInSourceGeneratedDocument() - { - var spans = await GetTagSpansInSourceGeneratedDocumentAsync("class C {"); - var firstSpan = Assert.Single(spans); - Assert.Equal(PredefinedErrorTypeNames.SyntaxError, firstSpan.Tag.ErrorType); - } + [WpfFact] + public async Task ErrorTagGeneratedForErrorInSourceGeneratedDocument() + { + var spans = await GetTagSpansInSourceGeneratedDocumentAsync("class C {"); + var firstSpan = Assert.Single(spans); + Assert.Equal(PredefinedErrorTypeNames.SyntaxError, firstSpan.Tag.ErrorType); + } - private static async Task>> GetTagSpansAsync(string content) - { - using var workspace = EditorTestWorkspace.CreateCSharp(content, composition: SquiggleUtilities.WpfCompositionWithSolutionCrawler); - Assert.True(workspace.TryApplyChanges(workspace.CurrentSolution.WithOptions( - workspace.CurrentSolution.Options.WithChangedOption(new OptionKey(DiagnosticOptionsStorage.PullDiagnosticsFeatureFlag), false)))); - return await GetTagSpansAsync(workspace); - } + private static async Task>> GetTagSpansAsync(string content) + { + using var workspace = EditorTestWorkspace.CreateCSharp(content, composition: SquiggleUtilities.WpfCompositionWithSolutionCrawler); + Assert.True(workspace.TryApplyChanges(workspace.CurrentSolution.WithOptions( + workspace.CurrentSolution.Options.WithChangedOption(new OptionKey(DiagnosticOptionsStorage.PullDiagnosticsFeatureFlag), false)))); + return await GetTagSpansAsync(workspace); + } - private static async Task>> GetTagSpansInSourceGeneratedDocumentAsync(string content) - { - using var workspace = EditorTestWorkspace.CreateCSharp( - files: [], - sourceGeneratedFiles: new[] { content }, - composition: SquiggleUtilities.WpfCompositionWithSolutionCrawler); - Assert.True(workspace.TryApplyChanges(workspace.CurrentSolution.WithOptions( - workspace.CurrentSolution.Options.WithChangedOption(new OptionKey(DiagnosticOptionsStorage.PullDiagnosticsFeatureFlag), false)))); + private static async Task>> GetTagSpansInSourceGeneratedDocumentAsync(string content) + { + using var workspace = EditorTestWorkspace.CreateCSharp( + files: [], + sourceGeneratedFiles: new[] { content }, + composition: SquiggleUtilities.WpfCompositionWithSolutionCrawler); + Assert.True(workspace.TryApplyChanges(workspace.CurrentSolution.WithOptions( + workspace.CurrentSolution.Options.WithChangedOption(new OptionKey(DiagnosticOptionsStorage.PullDiagnosticsFeatureFlag), false)))); - return await GetTagSpansAsync(workspace); - } + return await GetTagSpansAsync(workspace); + } - private static async Task>> GetTagSpansAsync(EditorTestWorkspace workspace) - { - workspace.GlobalOptions.SetGlobalOption(InlineDiagnosticsOptionsStorage.EnableInlineDiagnostics, LanguageNames.CSharp, true); - return (await TestDiagnosticTagProducer.GetDiagnosticsAndErrorSpans(workspace)).Item2; - } + private static async Task>> GetTagSpansAsync(EditorTestWorkspace workspace) + { + workspace.GlobalOptions.SetGlobalOption(InlineDiagnosticsOptionsStorage.EnableInlineDiagnostics, LanguageNames.CSharp, true); + return (await TestDiagnosticTagProducer.GetDiagnosticsAndErrorSpans(workspace)).Item2; } } diff --git a/src/EditorFeatures/Core.Wpf/Diagnostics/UnnecessaryCodeFormatDefinition.cs b/src/EditorFeatures/Core.Wpf/Diagnostics/UnnecessaryCodeFormatDefinition.cs deleted file mode 100644 index c7e847712ca55..0000000000000 --- a/src/EditorFeatures/Core.Wpf/Diagnostics/UnnecessaryCodeFormatDefinition.cs +++ /dev/null @@ -1,35 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -#nullable disable - -using System; -using System.Collections.Generic; -using System.ComponentModel.Composition; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using Microsoft.CodeAnalysis.Diagnostics; -using Microsoft.CodeAnalysis.Host.Mef; -using Microsoft.VisualStudio.Text.Classification; -using Microsoft.VisualStudio.Utilities; - -namespace Microsoft.CodeAnalysis.Editor.Diagnostics -{ - [Export(typeof(EditorFormatDefinition))] - [ClassificationType(ClassificationTypeNames = ClassificationTypeDefinitions.UnnecessaryCode)] - [Name(ClassificationTypeDefinitions.UnnecessaryCode)] - [Order(After = Priority.High)] - [UserVisible(false)] - internal sealed class UnnecessaryCodeFormatDefinition : ClassificationFormatDefinition - { - [ImportingConstructor] - [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] - public UnnecessaryCodeFormatDefinition() - { - this.DisplayName = EditorFeaturesResources.Unnecessary_Code; - this.ForegroundOpacity = 0.6; - } - } -} diff --git a/src/EditorFeatures/Core/Diagnostics/AbstractDiagnosticsTaggerProvider.SingleDiagnosticKindPullTaggerProvider.cs b/src/EditorFeatures/Core.Wpf/InlineDiagnostics/AbstractDiagnosticsTaggerProvider.SingleDiagnosticKindPullTaggerProvider.cs similarity index 99% rename from src/EditorFeatures/Core/Diagnostics/AbstractDiagnosticsTaggerProvider.SingleDiagnosticKindPullTaggerProvider.cs rename to src/EditorFeatures/Core.Wpf/InlineDiagnostics/AbstractDiagnosticsTaggerProvider.SingleDiagnosticKindPullTaggerProvider.cs index d9837b2056422..3016dfbede679 100644 --- a/src/EditorFeatures/Core/Diagnostics/AbstractDiagnosticsTaggerProvider.SingleDiagnosticKindPullTaggerProvider.cs +++ b/src/EditorFeatures/Core.Wpf/InlineDiagnostics/AbstractDiagnosticsTaggerProvider.SingleDiagnosticKindPullTaggerProvider.cs @@ -91,9 +91,6 @@ protected sealed override Task ProduceTagsAsync( private async Task ProduceTagsAsync( TaggerContext context, DocumentSnapshotSpan documentSpanToTag, CancellationToken cancellationToken) { - if (!_callback.IsEnabled) - return; - var document = documentSpanToTag.Document; if (document == null) return; diff --git a/src/EditorFeatures/Core/Diagnostics/AbstractDiagnosticsTaggerProvider.cs b/src/EditorFeatures/Core.Wpf/InlineDiagnostics/AbstractDiagnosticsTaggerProvider.cs similarity index 78% rename from src/EditorFeatures/Core/Diagnostics/AbstractDiagnosticsTaggerProvider.cs rename to src/EditorFeatures/Core.Wpf/InlineDiagnostics/AbstractDiagnosticsTaggerProvider.cs index 1155c78f348bc..bcc13f909297d 100644 --- a/src/EditorFeatures/Core/Diagnostics/AbstractDiagnosticsTaggerProvider.cs +++ b/src/EditorFeatures/Core.Wpf/InlineDiagnostics/AbstractDiagnosticsTaggerProvider.cs @@ -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 Options { get; } protected virtual ImmutableArray FeatureOptions { get; } = []; - protected abstract bool IsEnabled { get; } - protected abstract bool IncludeDiagnostic(DiagnosticData data); protected abstract bool TagEquals(TTag tag1, TTag tag2); - protected abstract ITagSpan? CreateTagSpan(Workspace workspace, SnapshotSpan span, DiagnosticData data); + + protected abstract TTag? CreateTag(Workspace workspace, DiagnosticData diagnostic); /// /// Get the 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? CreateTagSpan(Workspace workspace, SnapshotSpan span, DiagnosticData data) + { + 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(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); + } } diff --git a/src/EditorFeatures/Core.Wpf/InlineDiagnostics/InlineDiagnosticsTaggerProvider.cs b/src/EditorFeatures/Core.Wpf/InlineDiagnostics/InlineDiagnosticsTaggerProvider.cs index 346bcba274ce1..3ad67867ea199 100644 --- a/src/EditorFeatures/Core.Wpf/InlineDiagnostics/InlineDiagnosticsTaggerProvider.cs +++ b/src/EditorFeatures/Core.Wpf/InlineDiagnostics/InlineDiagnosticsTaggerProvider.cs @@ -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; + +[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( + threadingContext, + diagnosticService, + analyzerService, + globalOptions, + visibilityTracker, + listenerProvider.GetListener(FeatureAttribute.ErrorSquiggles)) { - [Export(typeof(ITaggerProvider))] - [ContentType(ContentTypeNames.RoslynContentType)] - [TagType(typeof(InlineDiagnosticsTag))] - internal sealed class InlineDiagnosticsTaggerProvider : AbstractDiagnosticsAdornmentTaggerProvider - { - 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 Options { get; } = [InlineDiagnosticsOptionsStorage.EnableInlineDiagnostics]; - protected sealed override ImmutableArray FeatureOptions { get; } = [InlineDiagnosticsOptionsStorage.Location]; + protected sealed override ImmutableArray Options { get; } = [InlineDiagnosticsOptionsStorage.EnableInlineDiagnostics]; + protected sealed override ImmutableArray 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(); + return new InlineDiagnosticsTag(errorType, diagnostic, _editorFormatMap, _classificationFormatMapService, + _classificationTypeRegistryService, locationOption, navigateService); + } - var locationOption = GlobalOptions.GetOption(InlineDiagnosticsOptionsStorage.Location, project.Language); - var navigateService = workspace.Services.GetRequiredService(); - 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; } - - /// - /// 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. - /// - /// 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. - /// - /// - protected sealed override bool TagEquals(InlineDiagnosticsTag tag1, InlineDiagnosticsTag tag2) - => tag1 == tag2; } + + /// + /// 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. + /// + /// 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. + /// + /// + protected sealed override bool TagEquals(InlineDiagnosticsTag tag1, InlineDiagnosticsTag tag2) + => tag1 == tag2; } diff --git a/src/EditorFeatures/Core/Diagnostics/AbstractDiagnosticsAdornmentTaggerProvider.RoslynErrorTag.cs b/src/EditorFeatures/Core/Diagnostics/AbstractDiagnosticsAdornmentTaggerProvider.RoslynErrorTag.cs deleted file mode 100644 index 42a1bc01b9726..0000000000000 --- a/src/EditorFeatures/Core/Diagnostics/AbstractDiagnosticsAdornmentTaggerProvider.RoslynErrorTag.cs +++ /dev/null @@ -1,64 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// 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 Microsoft.CodeAnalysis.Classification; -using Microsoft.CodeAnalysis.Editor.Implementation.IntelliSense.QuickInfo; -using Microsoft.VisualStudio.Text.Adornments; -using Microsoft.VisualStudio.Text.Tagging; -using Roslyn.Utilities; - -namespace Microsoft.CodeAnalysis.Diagnostics; - -internal partial class AbstractDiagnosticsAdornmentTaggerProvider -{ - protected sealed class RoslynErrorTag(string errorType, Workspace workspace, DiagnosticData data) : ErrorTag(errorType, CreateToolTipContent(workspace, data)), IEquatable - { - private readonly DiagnosticData _data = data; - - private static object CreateToolTipContent(Workspace workspace, DiagnosticData diagnostic) - { - Action? navigationAction = null; - string? tooltip = null; - if (workspace != null) - { - var helpLinkUri = diagnostic.GetValidHelpLinkUri(); - if (helpLinkUri != null) - { - navigationAction = new QuickInfoHyperLink(workspace, helpLinkUri).NavigationAction; - tooltip = diagnostic.HelpLink; - } - } - - var diagnosticIdTextRun = navigationAction is null - ? new ClassifiedTextRun(ClassificationTypeNames.Text, diagnostic.Id) - : new ClassifiedTextRun(ClassificationTypeNames.Text, diagnostic.Id, navigationAction, tooltip); - - return new ContainerElement( - ContainerElementStyle.Wrapped, - new ClassifiedTextElement( - diagnosticIdTextRun, - new ClassifiedTextRun(ClassificationTypeNames.Punctuation, ":"), - new ClassifiedTextRun(ClassificationTypeNames.WhiteSpace, " "), - new ClassifiedTextRun(ClassificationTypeNames.Text, diagnostic.Message))); - } - - public override bool Equals(object? obj) - => Equals(obj as RoslynErrorTag); - - public bool Equals(RoslynErrorTag? other) - { - return other != null && - this.ErrorType == other.ErrorType && - this._data.HelpLink == other._data.HelpLink && - this._data.Id == other._data.Id && - this._data.Message == other._data.Message; - } - - // Intentionally throwing, we have never supported this facility, and there is no contract around placing - // these tags in sets or maps. - public override int GetHashCode() - => throw new NotImplementedException(); - } -} diff --git a/src/EditorFeatures/Core/Diagnostics/AbstractDiagnosticsAdornmentTaggerProvider.cs b/src/EditorFeatures/Core/Diagnostics/AbstractDiagnosticsAdornmentTaggerProvider.cs deleted file mode 100644 index 8b14f8ea2b33f..0000000000000 --- a/src/EditorFeatures/Core/Diagnostics/AbstractDiagnosticsAdornmentTaggerProvider.cs +++ /dev/null @@ -1,65 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// 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 Microsoft.CodeAnalysis.Editor.Shared.Utilities; -using Microsoft.CodeAnalysis.Options; -using Microsoft.CodeAnalysis.Shared.TestHooks; -using Microsoft.CodeAnalysis.Workspaces; -using Microsoft.VisualStudio.Text; -using Microsoft.VisualStudio.Text.Tagging; - -namespace Microsoft.CodeAnalysis.Diagnostics; - -internal abstract partial class AbstractDiagnosticsAdornmentTaggerProvider : - AbstractDiagnosticsTaggerProvider - where TTag : class, ITag -{ - protected AbstractDiagnosticsAdornmentTaggerProvider( - IThreadingContext threadingContext, - IDiagnosticService diagnosticService, - IDiagnosticAnalyzerService analyzerService, - IGlobalOptionService globalOptions, - ITextBufferVisibilityTracker? visibilityTracker, - IAsynchronousOperationListenerProvider listenerProvider) - : base(threadingContext, diagnosticService, analyzerService, globalOptions, visibilityTracker, listenerProvider.GetListener(FeatureAttribute.ErrorSquiggles)) - { - } - - protected abstract TTag? CreateTag(Workspace workspace, DiagnosticData diagnostic); - - protected sealed override bool IsEnabled => true; - - protected sealed override ITagSpan? CreateTagSpan(Workspace workspace, SnapshotSpan span, DiagnosticData data) - { - 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(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); - } -} diff --git a/src/EditorFeatures/Core/Diagnostics/ClassificationTypeDefinitions.cs b/src/EditorFeatures/Core/Diagnostics/ClassificationTypeDefinitions.cs deleted file mode 100644 index 7778e3a4aa66a..0000000000000 --- a/src/EditorFeatures/Core/Diagnostics/ClassificationTypeDefinitions.cs +++ /dev/null @@ -1,22 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -#nullable disable - -using System.ComponentModel.Composition; -using Microsoft.VisualStudio.Language.StandardClassification; -using Microsoft.VisualStudio.Text.Classification; -using Microsoft.VisualStudio.Utilities; - -namespace Microsoft.CodeAnalysis.Diagnostics; - -internal sealed class ClassificationTypeDefinitions -{ - public const string UnnecessaryCode = "unnecessary code"; - - [Export] - [Name(UnnecessaryCode)] - [BaseDefinition(PredefinedClassificationTypeNames.FormalLanguage)] - internal ClassificationTypeDefinition UnnecessaryCodeTypeDefinition { get; set; } -} diff --git a/src/EditorFeatures/Core/Diagnostics/DiagnosticsOptionsStorage.cs b/src/EditorFeatures/Core/Diagnostics/DiagnosticsOptionsStorage.cs deleted file mode 100644 index 1069c1b0238ea..0000000000000 --- a/src/EditorFeatures/Core/Diagnostics/DiagnosticsOptionsStorage.cs +++ /dev/null @@ -1,14 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using Microsoft.CodeAnalysis.Options; - -namespace Microsoft.CodeAnalysis.Diagnostics; - -internal static class DiagnosticsOptionsStorage -{ - public static readonly Option2 Classification = new("dotnet_enable_classification", defaultValue: true); - - public static readonly Option2 Squiggles = new("dotnet_enable_squiggles", defaultValue: true); -} diff --git a/src/EditorFeatures/TestUtilities/Squiggles/SquiggleUtilities.cs b/src/EditorFeatures/TestUtilities/Squiggles/SquiggleUtilities.cs index 30a907a5e0930..e02b08c8d533d 100644 --- a/src/EditorFeatures/TestUtilities/Squiggles/SquiggleUtilities.cs +++ b/src/EditorFeatures/TestUtilities/Squiggles/SquiggleUtilities.cs @@ -31,7 +31,7 @@ public static class SquiggleUtilities internal static async Task<(ImmutableArray, ImmutableArray>)> GetDiagnosticsAndErrorSpansAsync( EditorTestWorkspace workspace, IReadOnlyDictionary> analyzerMap = null) - where TProvider : AbstractDiagnosticsAdornmentTaggerProvider + where TProvider : AbstractDiagnosticsTaggerProvider where TTag : class, ITag { using var wrapper = new DiagnosticTaggerWrapper(workspace, analyzerMap); diff --git a/src/EditorFeatures/TestUtilities/Squiggles/TestDiagnosticTagProducer.cs b/src/EditorFeatures/TestUtilities/Squiggles/TestDiagnosticTagProducer.cs index c78608a989456..bf3decb1a87f4 100644 --- a/src/EditorFeatures/TestUtilities/Squiggles/TestDiagnosticTagProducer.cs +++ b/src/EditorFeatures/TestUtilities/Squiggles/TestDiagnosticTagProducer.cs @@ -10,87 +10,85 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Editor.UnitTests.Diagnostics; -using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Test.Utilities; using Microsoft.CodeAnalysis.Text; using Microsoft.CodeAnalysis.Text.Shared.Extensions; using Microsoft.VisualStudio.Text.Tagging; using Roslyn.Utilities; -namespace Microsoft.CodeAnalysis.Editor.UnitTests.Squiggles +namespace Microsoft.CodeAnalysis.Editor.UnitTests.Squiggles; + +internal sealed class TestDiagnosticTagProducer + where TProvider : AbstractDiagnosticsTaggerProvider + where TTag : class, ITag { - internal sealed class TestDiagnosticTagProducer - where TProvider : AbstractDiagnosticsAdornmentTaggerProvider - where TTag : class, ITag + internal static Task<(ImmutableArray, ImmutableArray>)> GetDiagnosticsAndErrorSpans( + EditorTestWorkspace workspace, + IReadOnlyDictionary>? analyzerMap = null) { - internal static Task<(ImmutableArray, ImmutableArray>)> GetDiagnosticsAndErrorSpans( - EditorTestWorkspace workspace, - IReadOnlyDictionary>? analyzerMap = null) - { - return SquiggleUtilities.GetDiagnosticsAndErrorSpansAsync(workspace, analyzerMap); - } + return SquiggleUtilities.GetDiagnosticsAndErrorSpansAsync(workspace, analyzerMap); + } - internal static async Task>> GetErrorsFromUpdateSource(EditorTestWorkspace workspace, DiagnosticsUpdatedArgs updateArgs, DiagnosticKind diagnosticKind) - { - var source = new TestDiagnosticUpdateSource(); + internal static async Task>> GetErrorsFromUpdateSource(EditorTestWorkspace workspace, DiagnosticsUpdatedArgs updateArgs, DiagnosticKind diagnosticKind) + { + var source = new TestDiagnosticUpdateSource(); - using var wrapper = new DiagnosticTaggerWrapper(workspace, updateSource: source); + using var wrapper = new DiagnosticTaggerWrapper(workspace, updateSource: source); - var firstDocument = workspace.Documents.First(); - var tagger = wrapper.TaggerProvider.CreateTagger(firstDocument.GetTextBuffer()); - using var disposable = (IDisposable)tagger; + var firstDocument = workspace.Documents.First(); + var tagger = wrapper.TaggerProvider.CreateTagger(firstDocument.GetTextBuffer()); + using var disposable = (IDisposable)tagger; - var analyzerServer = (MockDiagnosticAnalyzerService)workspace.GetService(); - analyzerServer.AddDiagnostics(updateArgs.Diagnostics, diagnosticKind); + var analyzerServer = (MockDiagnosticAnalyzerService)workspace.GetService(); + analyzerServer.AddDiagnostics(updateArgs.Diagnostics, diagnosticKind); - source.RaiseDiagnosticsUpdated(ImmutableArray.Create(updateArgs)); + source.RaiseDiagnosticsUpdated(ImmutableArray.Create(updateArgs)); - await wrapper.WaitForTags(); + await wrapper.WaitForTags(); - var snapshot = firstDocument.GetTextBuffer().CurrentSnapshot; - var spans = tagger.GetTags(snapshot.GetSnapshotSpanCollection()).ToImmutableArray(); + var snapshot = firstDocument.GetTextBuffer().CurrentSnapshot; + var spans = tagger.GetTags(snapshot.GetSnapshotSpanCollection()).ToImmutableArray(); - return spans; - } + return spans; + } - internal static DiagnosticData CreateDiagnosticData(EditorTestHostDocument document, TextSpan span) - { - Contract.ThrowIfNull(document.FilePath); - - var sourceText = document.GetTextBuffer().CurrentSnapshot.AsText(); - var linePosSpan = sourceText.Lines.GetLinePositionSpan(span); - return new DiagnosticData( - id: "test", - category: "test", - message: "test", - severity: DiagnosticSeverity.Error, - defaultSeverity: DiagnosticSeverity.Error, - isEnabledByDefault: true, - warningLevel: 0, - projectId: document.Project.Id, - customTags: ImmutableArray.Empty, - properties: ImmutableDictionary.Empty, - location: new DiagnosticDataLocation(new FileLinePositionSpan(document.FilePath, linePosSpan), document.Id), - language: document.Project.Language); - } + internal static DiagnosticData CreateDiagnosticData(EditorTestHostDocument document, TextSpan span) + { + Contract.ThrowIfNull(document.FilePath); + + var sourceText = document.GetTextBuffer().CurrentSnapshot.AsText(); + var linePosSpan = sourceText.Lines.GetLinePositionSpan(span); + return new DiagnosticData( + id: "test", + category: "test", + message: "test", + severity: DiagnosticSeverity.Error, + defaultSeverity: DiagnosticSeverity.Error, + isEnabledByDefault: true, + warningLevel: 0, + projectId: document.Project.Id, + customTags: ImmutableArray.Empty, + properties: ImmutableDictionary.Empty, + location: new DiagnosticDataLocation(new FileLinePositionSpan(document.FilePath, linePosSpan), document.Id), + language: document.Project.Language); + } - private class TestDiagnosticUpdateSource : IDiagnosticUpdateSource - { - private ImmutableArray _diagnostics = ImmutableArray.Empty; + private class TestDiagnosticUpdateSource : IDiagnosticUpdateSource + { + private ImmutableArray _diagnostics = ImmutableArray.Empty; - public void RaiseDiagnosticsUpdated(ImmutableArray args) - { - _diagnostics = args.SelectManyAsArray(e => e.Diagnostics); - DiagnosticsUpdated?.Invoke(this, args); - } + public void RaiseDiagnosticsUpdated(ImmutableArray args) + { + _diagnostics = args.SelectManyAsArray(e => e.Diagnostics); + DiagnosticsUpdated?.Invoke(this, args); + } - public event EventHandler>? DiagnosticsUpdated; - public event EventHandler DiagnosticsCleared { add { } remove { } } + public event EventHandler>? DiagnosticsUpdated; + public event EventHandler DiagnosticsCleared { add { } remove { } } - public bool SupportGetDiagnostics => false; + public bool SupportGetDiagnostics => false; - public ValueTask> GetDiagnosticsAsync(Workspace workspace, ProjectId? projectId, DocumentId? documentId, object? id, bool includeSuppressedDiagnostics = false, CancellationToken cancellationToken = default) - => new(includeSuppressedDiagnostics ? _diagnostics : _diagnostics.WhereAsArray(d => !d.IsSuppressed)); - } + public ValueTask> GetDiagnosticsAsync(Workspace workspace, ProjectId? projectId, DocumentId? documentId, object? id, bool includeSuppressedDiagnostics = false, CancellationToken cancellationToken = default) + => new(includeSuppressedDiagnostics ? _diagnostics : _diagnostics.WhereAsArray(d => !d.IsSuppressed)); } } diff --git a/src/VisualStudio/Core/Def/Options/VisualStudioOptionStorage.cs b/src/VisualStudio/Core/Def/Options/VisualStudioOptionStorage.cs index 350c638477829..669a1e31877f5 100644 --- a/src/VisualStudio/Core/Def/Options/VisualStudioOptionStorage.cs +++ b/src/VisualStudio/Core/Def/Options/VisualStudioOptionStorage.cs @@ -359,7 +359,6 @@ public bool TryFetch(LocalUserRegistryOptionPersister persister, OptionKey2 opti {"visual_studio_enable_file_logging_for_diagnostics", new LocalUserProfileStorage(@"Roslyn\Internal\Diagnostics", "EnableFileLoggingForDiagnostics")}, {"dotnet_enable_automatic_line_ender", new LocalUserProfileStorage(@"Roslyn\Internal\OnOff\Features", "Automatic Line Ender")}, {"dotnet_enable_brace_matching", new LocalUserProfileStorage(@"Roslyn\Internal\OnOff\Features", "Brace Matching")}, - {"dotnet_enable_classification", new LocalUserProfileStorage(@"Roslyn\Internal\OnOff\Features", "Classification")}, {"dotnet_enable_event_hook_up", new LocalUserProfileStorage(@"Roslyn\Internal\OnOff\Features", "Event Hookup")}, {"dotnet_format_on_save", new LocalUserProfileStorage(@"Roslyn\Internal\OnOff\Features", "FormatOnSave")}, {"dotnet_enable_full_solution_analysis_memory_monitor", new LocalUserProfileStorage(@"Roslyn\Internal\OnOff\Features", "Full Solution Analysis Memory Monitor")}, @@ -372,7 +371,6 @@ public bool TryFetch(LocalUserRegistryOptionPersister persister, OptionKey2 opti {"dotnet_show_intellicode_debug_info", new LocalUserProfileStorage(@"Roslyn\Internal\OnOff\Features", "ShowDebugInfo")}, {"dotnet_enable_smart_indenter", new LocalUserProfileStorage(@"Roslyn\Internal\OnOff\Features", "Smart Indenter")}, {"dotnet_enable_snippets", new LocalUserProfileStorage(@"Roslyn\Internal\OnOff\Features", "Snippets2")}, - {"dotnet_enable_squiggles", new LocalUserProfileStorage(@"Roslyn\Internal\OnOff\Features", "Squiggles")}, {"dotnet_enable_syntactic_colorizer", new LocalUserProfileStorage(@"Roslyn\Internal\OnOff\Features", "Syntactic Colorizer")}, {"dotnet_enable_solution_crawler", new LocalUserProfileStorage(@"Roslyn\Internal\SolutionCrawler", "Solution Crawler")}, {"dotnet_colorize_json_patterns", new RoamingProfileStorage("TextEditor.%LANGUAGE%.Specific.ColorizeJsonPatterns")},