// 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.Collections.Generic;
using System.Collections.Immutable;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CodeActions;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Editor.Host;
using Microsoft.CodeAnalysis.Editor.Shared.Extensions;
using Microsoft.CodeAnalysis.Extensions;
using Microsoft.CodeAnalysis.PooledObjects;
using Microsoft.CodeAnalysis.Text;
using Microsoft.VisualStudio.Language.Intellisense;
using Microsoft.VisualStudio.Text;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.Editor.Implementation.Suggestions
{
///
/// Base type for all SuggestedActions that have 'flavors'. 'Flavors' are child actions that
/// are presented as simple links, not as menu-items, in the light-bulb. Examples of 'flavors'
/// include 'preview changes' (for refactorings and fixes) and 'fix all in document, project, solution'
/// (for fixes).
///
/// Because all derivations support 'preview changes', we bake that logic into this base type.
///
internal abstract partial class SuggestedActionWithNestedFlavors : SuggestedAction, ISuggestedActionWithFlavors
{
private readonly SuggestedActionSet _additionalFlavors;
private ImmutableArray _nestedFlavors;
public SuggestedActionWithNestedFlavors(
SuggestedActionsSourceProvider sourceProvider,
Workspace workspace, ITextBuffer subjectBuffer,
object provider, CodeAction codeAction,
SuggestedActionSet additionalFlavors = null)
: base(sourceProvider, workspace, subjectBuffer,
provider, codeAction)
{
_additionalFlavors = additionalFlavors;
}
///
/// HasActionSets is always true because we always know we provide 'preview changes'.
///
public sealed override bool HasActionSets => true;
public async sealed override Task> GetActionSetsAsync(CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
// Light bulb will always invoke this property on the UI thread.
AssertIsForeground();
if (_nestedFlavors.IsDefault)
{
var extensionManager = this.Workspace.Services.GetService();
// We use ConfigureAwait(true) to stay on the UI thread.
_nestedFlavors = await extensionManager.PerformFunctionAsync(
Provider, () => CreateAllFlavors(cancellationToken),
defaultValue: ImmutableArray.Empty).ConfigureAwait(true);
}
Contract.ThrowIfTrue(_nestedFlavors.IsDefault);
return _nestedFlavors;
}
private async Task> CreateAllFlavors(CancellationToken cancellationToken)
{
var builder = ArrayBuilder.GetInstance();
// We use ConfigureAwait(true) to stay on the UI thread.
var previewChangesSuggestedActionSet = await GetPreviewChangesFlavor(cancellationToken).ConfigureAwait(true);
if (previewChangesSuggestedActionSet != null)
{
builder.Add(previewChangesSuggestedActionSet);
}
if (_additionalFlavors != null)
{
builder.Add(_additionalFlavors);
}
return builder.ToImmutableAndFree();
}
private async Task GetPreviewChangesFlavor(CancellationToken cancellationToken)
{
// We use ConfigureAwait(true) to stay on the UI thread.
var previewChangesAction = await PreviewChangesSuggestedAction.CreateAsync(
this, cancellationToken).ConfigureAwait(true);
if (previewChangesAction == null)
{
return null;
}
return new SuggestedActionSet(ImmutableArray.Create(previewChangesAction));
}
// HasPreview is called synchronously on the UI thread. In order to avoid blocking the UI thread,
// we need to provide a 'quick' answer here as opposed to the 'right' answer. Providing the 'right'
// answer is expensive (because we will need to call CodeAction.GetPreviewOperationsAsync() for this
// and this will involve computing the changed solution for the ApplyChangesOperation for the fix /
// refactoring). So we always return 'true' here (so that platform will call GetActionSetsAsync()
// below). Platform guarantees that nothing bad will happen if we return 'true' here and later return
// 'null' / empty collection from within GetPreviewAsync().
public override bool HasPreview => true;
public override async Task