// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Globalization; using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.Editor.Host; using Microsoft.CodeAnalysis.Shared.TestHooks; using Microsoft.CodeAnalysis.Shared.Utilities; using Microsoft.VisualStudio.Language.Intellisense; using Microsoft.VisualStudio.Text; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Editor.Implementation.Suggestions { /// /// Represents top-level light bulb menu item for the suppression fix. /// The top-level item itself does nothing. It doesn't display a preview and can't be invoked / applied. /// The top-level item is simply a container for the fixes displayed as sub-menu items. /// internal sealed class SuppressionSuggestedAction : SuggestedAction, ITelemetryDiagnosticID { private readonly CodeFix _fix; private readonly Func _getFixAllSuggestedActionSet; public SuppressionSuggestedAction( Workspace workspace, ITextBuffer subjectBuffer, ICodeActionEditHandlerService editHandler, IWaitIndicator waitIndicator, CodeFix fix, object provider, Func getFixAllSuggestedActionSet, IAsynchronousOperationListener operationListener) : base(workspace, subjectBuffer, editHandler, waitIndicator, provider, operationListener, fix.Action) { _fix = fix; _getFixAllSuggestedActionSet = getFixAllSuggestedActionSet; } // Put suppressions at the end of everything. internal override CodeActionPriority Priority => CodeActionPriority.None; public override bool HasActionSets { get { return this.CodeAction.GetCodeActions().Any(); } } private IEnumerable _actionSets; public override Task> GetActionSetsAsync(CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); if (_actionSets != null) { return Task.FromResult(_actionSets); } if (this.CodeAction.GetCodeActions().Any()) { var nestedSuggestedActions = ArrayBuilder.GetInstance(); var fixCount = this.CodeAction.GetCodeActions().Length; foreach (var action in this.CodeAction.GetCodeActions()) { cancellationToken.ThrowIfCancellationRequested(); var fixAllSuggestedActionSet = _getFixAllSuggestedActionSet(action); nestedSuggestedActions.Add(new CodeFixSuggestedAction( this.Workspace, this.SubjectBuffer, this.EditHandler, this.WaitIndicator, new CodeFix(_fix.Project, action, _fix.Diagnostics), this.Provider, fixAllSuggestedActionSet, this.OperationListener, action)); } _actionSets = ImmutableArray.Create( new SuggestedActionSet(nestedSuggestedActions.ToImmutableAndFree())); return Task.FromResult(_actionSets); } return SpecializedTasks.Default>(); } protected override Task InvokeAsync( IProgressTracker progressTracker, CancellationToken cancellationToken) { // The top-level action cannot be invoked. // However, the nested sub-actions returned above can be. throw new NotSupportedException(string.Format(EditorFeaturesResources._0_does_not_support_the_1_operation_However_it_may_contain_nested_2_s_see_2_3_that_support_this_operation, nameof(SuppressionSuggestedAction), nameof(Invoke), nameof(ISuggestedAction), nameof(GetActionSetsAsync))); } public string GetDiagnosticID() { var diagnostic = _fix.PrimaryDiagnostic; // we log diagnostic id as it is if it is from us if (diagnostic.Descriptor.CustomTags.Any(t => t == WellKnownDiagnosticTags.Telemetry)) { return diagnostic.Id; } // if it is from third party, we use hashcode return diagnostic.Id.GetHashCode().ToString(CultureInfo.InvariantCulture); } } }