From d9f57256bb28bcbad031c81a75d1f37af91bb664 Mon Sep 17 00:00:00 2001 From: Manish Vasani Date: Mon, 28 Feb 2022 04:31:19 -0800 Subject: [PATCH 01/18] FixAll public API + core fix all related types + other workspace related changes --- .../DefaultFixAllProviderHelpers.cs | 2 +- .../DocumentBasedFixAllProvider.cs | 79 +----- .../CodeRefactoringProvider.cs | 8 + .../DocumentBasedFixAllProvider.cs | 242 ++++++++++++++++++ .../FixAllOccurences/FixAllContext.cs | 107 ++++++++ .../FixAllOccurences/FixAllContextHelper.cs | 36 +++ .../FixAllOccurences/FixAllLogger.cs | 120 +++++++++ .../FixAllOccurences/FixAllProvider.cs | 88 +++++++ .../FixAllOccurences/FixAllProviderInfo.cs | 48 ++++ .../FixAllOccurences/FixAllScope.cs | 21 ++ ...ommonDocumentBasedFixAllProviderHelpers.cs | 97 +++++++ .../Core/Portable/PublicAPI.Unshipped.txt | 30 +++ .../Compiler/Core/Log/FunctionId.cs | 5 + .../FixAll/FixAllContextExtensions.cs | 22 ++ .../CodeRefactorings/FixAll/FixAllState.cs | 115 +++++++++ ...yntaxEditorBasedCodeRefactoringProvider.cs | 84 ++++++ .../Core/WorkspaceExtensions.projitems | 7 +- .../Core/WorkspaceExtensionsResources.resx | 9 + .../xlf/WorkspaceExtensionsResources.cs.xlf | 15 ++ .../xlf/WorkspaceExtensionsResources.de.xlf | 15 ++ .../xlf/WorkspaceExtensionsResources.es.xlf | 15 ++ .../xlf/WorkspaceExtensionsResources.fr.xlf | 15 ++ .../xlf/WorkspaceExtensionsResources.it.xlf | 15 ++ .../xlf/WorkspaceExtensionsResources.ja.xlf | 15 ++ .../xlf/WorkspaceExtensionsResources.ko.xlf | 15 ++ .../xlf/WorkspaceExtensionsResources.pl.xlf | 15 ++ .../WorkspaceExtensionsResources.pt-BR.xlf | 15 ++ .../xlf/WorkspaceExtensionsResources.ru.xlf | 15 ++ .../xlf/WorkspaceExtensionsResources.tr.xlf | 15 ++ .../WorkspaceExtensionsResources.zh-Hans.xlf | 15 ++ .../WorkspaceExtensionsResources.zh-Hant.xlf | 15 ++ 31 files changed, 1236 insertions(+), 79 deletions(-) create mode 100644 src/Workspaces/Core/Portable/CodeRefactorings/FixAllOccurences/DocumentBasedFixAllProvider.cs create mode 100644 src/Workspaces/Core/Portable/CodeRefactorings/FixAllOccurences/FixAllContext.cs create mode 100644 src/Workspaces/Core/Portable/CodeRefactorings/FixAllOccurences/FixAllContextHelper.cs create mode 100644 src/Workspaces/Core/Portable/CodeRefactorings/FixAllOccurences/FixAllLogger.cs create mode 100644 src/Workspaces/Core/Portable/CodeRefactorings/FixAllOccurences/FixAllProvider.cs create mode 100644 src/Workspaces/Core/Portable/CodeRefactorings/FixAllOccurences/FixAllProviderInfo.cs create mode 100644 src/Workspaces/Core/Portable/CodeRefactorings/FixAllOccurences/FixAllScope.cs create mode 100644 src/Workspaces/Core/Portable/FixAll/CommonDocumentBasedFixAllProviderHelpers.cs create mode 100644 src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeRefactorings/FixAll/FixAllContextExtensions.cs create mode 100644 src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeRefactorings/FixAll/FixAllState.cs create mode 100644 src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeRefactorings/SyntaxEditorBasedCodeRefactoringProvider.cs diff --git a/src/Workspaces/Core/Portable/CodeFixes/FixAllOccurrences/DefaultFixAllProviderHelpers.cs b/src/Workspaces/Core/Portable/CodeFixes/FixAllOccurrences/DefaultFixAllProviderHelpers.cs index ed5dac078c342..3034c94f38f6a 100644 --- a/src/Workspaces/Core/Portable/CodeFixes/FixAllOccurrences/DefaultFixAllProviderHelpers.cs +++ b/src/Workspaces/Core/Portable/CodeFixes/FixAllOccurrences/DefaultFixAllProviderHelpers.cs @@ -41,7 +41,7 @@ internal static class DefaultFixAllProviderHelpers return CodeAction.Create( title, c => Task.FromResult(solution)); -#pragma warning disable RS0005 // Do not use generic 'CodeAction.Create' to create 'CodeAction' +#pragma warning restore RS0005 // Do not use generic 'CodeAction.Create' to create 'CodeAction' } private static Task GetDocumentFixesAsync(FixAllContext fixAllContext, FixAllContexts fixAllContextsAsync) diff --git a/src/Workspaces/Core/Portable/CodeFixes/FixAllOccurrences/DocumentBasedFixAllProvider.cs b/src/Workspaces/Core/Portable/CodeFixes/FixAllOccurrences/DocumentBasedFixAllProvider.cs index 991adf0e61bd2..05cd751f09bf5 100644 --- a/src/Workspaces/Core/Portable/CodeFixes/FixAllOccurrences/DocumentBasedFixAllProvider.cs +++ b/src/Workspaces/Core/Portable/CodeFixes/FixAllOccurrences/DocumentBasedFixAllProvider.cs @@ -14,6 +14,7 @@ using Microsoft.CodeAnalysis.Shared.Utilities; using Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; +using static Microsoft.CodeAnalysis.FixAll.CommonDocumentBasedFixAllProviderHelpers; namespace Microsoft.CodeAnalysis.CodeFixes { @@ -94,7 +95,7 @@ private async Task FixSingleContextAsync(Solution currentSolution, Fix var docIdToNewRootOrText = await GetFixedDocumentsAsync(fixAllContext, progressTracker, diagnostics).ConfigureAwait(false); // Finally, cleanup the new doc roots, and apply the results to the solution. - currentSolution = await CleanupAndApplyChangesAsync(fixAllContext, progressTracker, currentSolution, docIdToNewRootOrText).ConfigureAwait(false); + currentSolution = await CleanupAndApplyChangesAsync(progressTracker, currentSolution, docIdToNewRootOrText, fixAllContext.CancellationToken).ConfigureAwait(false); return currentSolution; } @@ -166,81 +167,5 @@ private static async Task> DetermineDiagnosticsAsync( return docIdToNewRootOrText; } - - /// - /// Take all the fixed documents and format/simplify/clean them up (if the language supports that), and take the - /// resultant text and apply it to the solution. If the language doesn't support cleanup, then just take the - /// given text and apply that instead. - /// - private static async Task CleanupAndApplyChangesAsync( - FixAllContext fixAllContext, - IProgressTracker progressTracker, - Solution currentSolution, - Dictionary docIdToNewRootOrText) - { - var cancellationToken = fixAllContext.CancellationToken; - using var _1 = progressTracker.ItemCompletedScope(); - - if (docIdToNewRootOrText.Count > 0) - { - // Next, go and insert those all into the solution so all the docs in this particular project point at - // the new trees (or text). At this point though, the trees have not been cleaned up. We don't cleanup - // the documents as they are created, or one at a time as we add them, as that would cause us to run - // cleanup on N different solution forks (which would be very expensive). Instead, by adding all the - // changed documents to one solution, and hten cleaning *those* we only perform cleanup semantics on one - // forked solution. - foreach (var (docId, (newRoot, newText)) in docIdToNewRootOrText) - { - currentSolution = newRoot != null - ? currentSolution.WithDocumentSyntaxRoot(docId, newRoot) - : currentSolution.WithDocumentText(docId, newText!); - } - - // Next, go and cleanup any trees we inserted. Once we clean the document, we get the text of it and - // insert that back into the final solution. This way we can release both the original fixed tree, and - // the cleaned tree (both of which can be much more expensive than just text). - // - // Do this in parallel across all the documents that were fixed. - using var _2 = ArrayBuilder>.GetInstance(out var tasks); - - foreach (var (docId, (newRoot, _)) in docIdToNewRootOrText) - { - if (newRoot != null) - { - var dirtyDocument = currentSolution.GetRequiredDocument(docId); - tasks.Add(Task.Run(async () => - { - var cleanedDocument = await PostProcessCodeAction.Instance.PostProcessChangesAsync(dirtyDocument, cancellationToken).ConfigureAwait(false); - var cleanedText = await cleanedDocument.GetTextAsync(cancellationToken).ConfigureAwait(false); - return (dirtyDocument.Id, cleanedText); - }, cancellationToken)); - } - } - - await Task.WhenAll(tasks).ConfigureAwait(false); - - // Finally, apply the cleaned documents to the solution. - foreach (var task in tasks) - { - var (docId, cleanedText) = await task.ConfigureAwait(false); - currentSolution = currentSolution.WithDocumentText(docId, cleanedText); - } - } - - return currentSolution; - } - - /// - /// Dummy class just to get access to - /// - private class PostProcessCodeAction : CodeAction - { - public static readonly PostProcessCodeAction Instance = new(); - - public override string Title => ""; - - public new Task PostProcessChangesAsync(Document document, CancellationToken cancellationToken) - => base.PostProcessChangesAsync(document, cancellationToken); - } } } diff --git a/src/Workspaces/Core/Portable/CodeRefactorings/CodeRefactoringProvider.cs b/src/Workspaces/Core/Portable/CodeRefactorings/CodeRefactoringProvider.cs index 0d5182ef66603..9f345c0ec9145 100644 --- a/src/Workspaces/Core/Portable/CodeRefactorings/CodeRefactoringProvider.cs +++ b/src/Workspaces/Core/Portable/CodeRefactorings/CodeRefactoringProvider.cs @@ -19,6 +19,14 @@ public abstract class CodeRefactoringProvider /// public abstract Task ComputeRefactoringsAsync(CodeRefactoringContext context); + /// + /// Gets an optional that can apply multiple occurrences of code refactoring(s) + /// registered by this code refactoring provider across the supported s. + /// Return null if the provider doesn't support fix all operation. + /// + public virtual FixAllProvider? GetFixAllProvider() + => null; + /// /// What priority this provider should run at. /// diff --git a/src/Workspaces/Core/Portable/CodeRefactorings/FixAllOccurences/DocumentBasedFixAllProvider.cs b/src/Workspaces/Core/Portable/CodeRefactorings/FixAllOccurences/DocumentBasedFixAllProvider.cs new file mode 100644 index 0000000000000..824ada5c52d27 --- /dev/null +++ b/src/Workspaces/Core/Portable/CodeRefactorings/FixAllOccurences/DocumentBasedFixAllProvider.cs @@ -0,0 +1,242 @@ +// 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 System.Collections.Generic; +using System.Collections.Immutable; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.CodeActions; +using Microsoft.CodeAnalysis.PooledObjects; +using Microsoft.CodeAnalysis.Shared.Extensions; +using Microsoft.CodeAnalysis.Shared.Utilities; +using Microsoft.CodeAnalysis.Text; +using Roslyn.Utilities; +using static Microsoft.CodeAnalysis.FixAll.CommonDocumentBasedFixAllProviderHelpers; + +namespace Microsoft.CodeAnalysis.CodeRefactorings +{ + using FixAllContexts = Func, Task>; + + /// + /// Provides a base class to write a for refactorings that fixes documents independently. + /// This type should be used in the case where the code refactoring(s) only affect individual s. + /// + /// + /// This type provides suitable logic for fixing large solutions in an efficient manner. Projects are serially + /// processed, with all the documents in the project being processed in parallel. + /// is invoked for each document for implementors to process. + /// + public abstract class DocumentBasedFixAllProvider : FixAllProvider + { + protected DocumentBasedFixAllProvider() + { + } + + /// + /// Produce a suitable title for the fix-all this type creates in . Override this if customizing that title is desired. + /// + protected virtual string GetFixAllTitle(FixAllContext fixAllContext) + => FixAllContextHelper.GetDefaultFixAllTitle(fixAllContext); + + /// + /// Apply fix all operation for the in the + /// for the given . The document returned will only be examined for its content + /// (e.g. it's or . No other aspects of document (like it's properties), + /// or changes to the or it points at will be considered. + /// + /// The context for the Fix All operation. + /// + /// The new representing the content fixed document. + /// -or- + /// , if no changes were made to the document. + /// + protected abstract Task FixAllAsync(FixAllContext fixAllContext); + + /// + /// Returns a bool indicating if the provider supports FixAll in selected span, + /// i.e. + /// + protected abstract bool SupportsFixAllForSelection { get; } + + /// + /// Returns a bool indicating if the provider supports FixAll in containing member, + /// i.e. + /// + protected abstract bool SupportsFixAllForContainingMember { get; } + + /// + /// Returns a bool indicating if the provider supports FixAll in containing type declaration, + /// i.e. + /// + protected abstract bool SupportsFixAllForContainingType { get; } + + public sealed override IEnumerable GetSupportedFixAllScopes() + { + foreach (var defaultScope in base.GetSupportedFixAllScopes()) + yield return defaultScope; + + if (SupportsFixAllForSelection) + yield return FixAllScope.Selection; + + if (SupportsFixAllForContainingMember) + yield return FixAllScope.ContainingMember; + + if (SupportsFixAllForContainingType) + yield return FixAllScope.ContainingType; + } + + public sealed override Task GetFixAsync(FixAllContext fixAllContext) + => GetFixAsync(FixAllContextHelper.GetDefaultFixAllTitle(fixAllContext), fixAllContext, FixAllContextsAsync); + + private static async Task GetFixAsync( + string title, FixAllContext fixAllContext, FixAllContexts fixAllContextsAsync) + { + Contract.ThrowIfFalse(fixAllContext.Scope is + FixAllScope.Document or FixAllScope.Project or FixAllScope.Solution or + FixAllScope.Selection or FixAllScope.ContainingMember or FixAllScope.ContainingType); + + var solution = fixAllContext.Scope switch + { + FixAllScope.Document or FixAllScope.Selection or + FixAllScope.ContainingMember or FixAllScope.ContainingType + => await GetDocumentFixesAsync(fixAllContext, fixAllContextsAsync).ConfigureAwait(false), + FixAllScope.Project + => await GetProjectFixesAsync(fixAllContext, fixAllContextsAsync).ConfigureAwait(false), + FixAllScope.Solution + => await GetSolutionFixesAsync(fixAllContext, fixAllContextsAsync).ConfigureAwait(false), + _ => throw ExceptionUtilities.UnexpectedValue(fixAllContext.Scope), + }; + + if (solution == null) + return null; + +#pragma warning disable RS0005 // Do not use generic 'CodeAction.Create' to create 'CodeAction' + + return CodeAction.Create( + title, c => Task.FromResult(solution)); + +#pragma warning restore RS0005 // Do not use generic 'CodeAction.Create' to create 'CodeAction' + } + + private static Task GetDocumentFixesAsync(FixAllContext fixAllContext, FixAllContexts fixAllContextsAsync) + => fixAllContextsAsync(fixAllContext, ImmutableArray.Create(fixAllContext)); + + private static Task GetProjectFixesAsync(FixAllContext fixAllContext, FixAllContexts fixAllContextsAsync) + => fixAllContextsAsync(fixAllContext, ImmutableArray.Create(fixAllContext.WithDocument(null))); + + private static Task GetSolutionFixesAsync(FixAllContext fixAllContext, FixAllContexts fixAllContextsAsync) + { + var solution = fixAllContext.Project.Solution; + var dependencyGraph = solution.GetProjectDependencyGraph(); + + // Walk through each project in topological order, determining and applying the diagnostics for each + // project. We do this in topological order so that the compilations for successive projects are readily + // available as we just computed them for dependent projects. If we were to do it out of order, we might + // start with a project that has a ton of dependencies, and we'd spend an inordinate amount of time just + // building the compilations for it before we could proceed. + // + // By processing one project at a time, we can also let go of a project once done with it, allowing us to + // reclaim lots of the memory so we don't overload the system while processing a large solution. + // + // Note: we have to filter down to projects of the same language as the FixAllContext points at a + // CodeFixProvider, and we can't call into providers of different languages with diagnostics from a + // different language. + var sortedProjects = dependencyGraph.GetTopologicallySortedProjects() + .Select(id => solution.GetRequiredProject(id)) + .Where(p => p.Language == fixAllContext.Project.Language); + return fixAllContextsAsync( + fixAllContext, + sortedProjects.SelectAsArray(p => fixAllContext.WithScope(FixAllScope.Project).WithProject(p).WithDocument(null))); + } + + private async Task FixAllContextsAsync(FixAllContext originalFixAllContext, ImmutableArray fixAllContexts) + { + var progressTracker = originalFixAllContext.GetProgressTracker(); + progressTracker.Description = this.GetFixAllTitle(originalFixAllContext); + + var solution = originalFixAllContext.Project.Solution; + + // We have 2 pieces of work per project. Computing code actions, and applying actions. + progressTracker.AddItems(fixAllContexts.Length * 2); + + // Process each context one at a time, allowing us to dump any information we computed for each once done with it. + var currentSolution = solution; + foreach (var fixAllContext in fixAllContexts) + { + Contract.ThrowIfFalse(fixAllContext.Scope is FixAllScope.Document or FixAllScope.Project or + FixAllScope.Selection or FixAllScope.ContainingMember or FixAllScope.ContainingType); + currentSolution = await FixSingleContextAsync(currentSolution, fixAllContext, progressTracker).ConfigureAwait(false); + } + + return currentSolution; + } + + private async Task FixSingleContextAsync(Solution currentSolution, FixAllContext fixAllContext, IProgressTracker progressTracker) + { + // First, get the fixes for all the diagnostics, and apply them to determine the new root/text for each doc. + var docIdToNewRootOrText = await GetFixedDocumentsAsync(fixAllContext, progressTracker).ConfigureAwait(false); + + // Finally, cleanup the new doc roots, and apply the results to the solution. + currentSolution = await CleanupAndApplyChangesAsync(progressTracker, currentSolution, docIdToNewRootOrText, fixAllContext.CancellationToken).ConfigureAwait(false); + + return currentSolution; + } + + /// + /// Attempts to apply fix all operations returning, for each updated document, either + /// the new syntax root for that document or its new text. Syntax roots are returned for documents that support + /// them, and are used to perform a final cleanup pass for formatting/simplication/etc. Text is returned for + /// documents that don't support syntax. + /// + private async Task> GetFixedDocumentsAsync( + FixAllContext fixAllContext, IProgressTracker progressTracker) + { + Contract.ThrowIfFalse(fixAllContext.Scope is FixAllScope.Document or FixAllScope.Project + or FixAllScope.Selection or FixAllScope.ContainingMember or FixAllScope.ContainingType); + + var cancellationToken = fixAllContext.CancellationToken; + + using var _1 = progressTracker.ItemCompletedScope(); + using var _2 = ArrayBuilder>.GetInstance(out var tasks); + + var docIdToNewRootOrText = new Dictionary(); + + // Process all documents in parallel to get the change for each doc. + var documentsToFix = fixAllContext.Scope == FixAllScope.Project + ? fixAllContext.Project.Documents + : SpecializedCollections.SingletonEnumerable(fixAllContext.Document); + + foreach (var document in documentsToFix) + { + tasks.Add(Task.Run(async () => + { + var newFixAllContext = fixAllContext.WithDocument(document); + var newDocument = await this.FixAllAsync(fixAllContext).ConfigureAwait(false); + if (newDocument == null || newDocument == document) + return default; + + // For documents that support syntax, grab the tree so that we can clean it up later. If it's a + // language that doesn't support that, then just grab the text. + var node = newDocument.SupportsSyntaxTree ? await newDocument.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false) : null; + var text = newDocument.SupportsSyntaxTree ? null : await newDocument.GetTextAsync(cancellationToken).ConfigureAwait(false); + + return (document.Id, (node, text)); + }, cancellationToken)); + } + + await Task.WhenAll(tasks).ConfigureAwait(false); + + foreach (var task in tasks) + { + var (docId, nodeOrText) = await task.ConfigureAwait(false); + if (docId != null) + docIdToNewRootOrText[docId] = nodeOrText; + } + + return docIdToNewRootOrText; + } + } +} diff --git a/src/Workspaces/Core/Portable/CodeRefactorings/FixAllOccurences/FixAllContext.cs b/src/Workspaces/Core/Portable/CodeRefactorings/FixAllOccurences/FixAllContext.cs new file mode 100644 index 0000000000000..98dc304173da2 --- /dev/null +++ b/src/Workspaces/Core/Portable/CodeRefactorings/FixAllOccurences/FixAllContext.cs @@ -0,0 +1,107 @@ +// 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 System.Collections.Generic; +using System.Collections.Immutable; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.CodeActions; +using Microsoft.CodeAnalysis.Shared.Utilities; +using Microsoft.CodeAnalysis.Text; +using Roslyn.Utilities; + +namespace Microsoft.CodeAnalysis.CodeRefactorings +{ + /// + /// Context for "Fix all occurrences" for code refactorings provided by each . + /// + public sealed class FixAllContext + { + internal FixAllState State { get; } + + internal FixAllProvider? FixAllProvider => State.FixAllProvider; + + /// + /// Document within which fix all occurrences was triggered. + /// + public Document Document => State.Document!; + + /// + /// Underlying which triggered this fix all. + /// + public CodeRefactoringProvider CodeRefactoringProvider => State.CodeRefactoringProvider; + + /// + /// to fix all occurrences. + /// + public FixAllScope Scope => State.FixAllScope; + + /// + /// Optional span within the to fix all occurrences. + /// This span can be non-null only when is any of the following document based scopes: + /// 1. + /// 2. + /// 3. + /// 4. + /// + public TextSpan? FixAllSpan => State.FixAllSpan; + + /// + /// The underlying for the code refactoring for which fix all occurences was triggered. + /// + public CodeAction CodeAction => State.CodeAction; + + /// + /// CancellationToken for fix all session. + /// + public CancellationToken CancellationToken { get; } + + internal IProgressTracker ProgressTracker { get; } + + /// + /// Project to fix all occurrences. + /// Note that this property will always be the containing project of + /// for publicly exposed FixAllContext instance. However, we might create an intermediate FixAllContext + /// with null and non-null Project, so we require this internal property for intermediate computation. + /// + internal Project Project => State.Project; + + internal FixAllContext( + FixAllState state, + IProgressTracker progressTracker, + CancellationToken cancellationToken) + { + State = state; + this.ProgressTracker = progressTracker; + this.CancellationToken = cancellationToken; + } + + /// + /// Gets a new with the given cancellationToken. + /// + public FixAllContext WithCancellationToken(CancellationToken cancellationToken) + { + if (this.CancellationToken == cancellationToken) + { + return this; + } + + return new FixAllContext(State, this.ProgressTracker, cancellationToken); + } + + internal FixAllContext WithDocument(Document? document) + => this.WithState(State.WithDocument(document)); + + internal FixAllContext WithProject(Project project) + => this.WithState(State.WithProject(project)); + + internal FixAllContext WithScope(FixAllScope scope) + => this.WithState(State.WithScope(scope)); + + private FixAllContext WithState(FixAllState state) + => this.State == state ? this : new FixAllContext(state, ProgressTracker, CancellationToken); + } +} diff --git a/src/Workspaces/Core/Portable/CodeRefactorings/FixAllOccurences/FixAllContextHelper.cs b/src/Workspaces/Core/Portable/CodeRefactorings/FixAllOccurences/FixAllContextHelper.cs new file mode 100644 index 0000000000000..7ca7ecd1a76de --- /dev/null +++ b/src/Workspaces/Core/Portable/CodeRefactorings/FixAllOccurences/FixAllContextHelper.cs @@ -0,0 +1,36 @@ +// 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.CodeActions; +using Microsoft.CodeAnalysis.Shared.Utilities; +using Roslyn.Utilities; + +namespace Microsoft.CodeAnalysis.CodeRefactorings +{ + internal static class FixAllContextHelper + { + public static string GetDefaultFixAllTitle(FixAllContext fixAllContext) + => GetDefaultFixAllTitle(fixAllContext.Scope, fixAllContext.CodeAction, fixAllContext.Document, fixAllContext.Project); + + public static string GetDefaultFixAllTitle( + FixAllScope fixAllScope, + CodeAction codeAction, + Document? triggerDocument, + Project triggerProject) + { + var title = codeAction.Title; + return fixAllScope switch + { + FixAllScope.Custom => string.Format(WorkspaceExtensionsResources.Fix_all_0, title), + FixAllScope.Document => string.Format(WorkspaceExtensionsResources.Fix_all_0_in_1, title, triggerDocument!.Name), + FixAllScope.Project => string.Format(WorkspaceExtensionsResources.Fix_all_0_in_1, title, triggerProject.Name), + FixAllScope.Solution => string.Format(WorkspaceExtensionsResources.Fix_all_0_in_Solution, title), + FixAllScope.Selection => string.Format(WorkspaceExtensionsResources.Fix_all_0_in_selection_for_1, title, triggerDocument!.Name), + FixAllScope.ContainingMember => string.Format(WorkspaceExtensionsResources.Fix_all_0_in_containing_member_for_1, title, triggerDocument!.Name), + FixAllScope.ContainingType => string.Format(WorkspaceExtensionsResources.Fix_all_0_in_containing_type_for_1, title, triggerDocument!.Name), + _ => throw ExceptionUtilities.UnexpectedValue(fixAllScope), + }; + } + } +} diff --git a/src/Workspaces/Core/Portable/CodeRefactorings/FixAllOccurences/FixAllLogger.cs b/src/Workspaces/Core/Portable/CodeRefactorings/FixAllOccurences/FixAllLogger.cs new file mode 100644 index 0000000000000..05f0d8091e44f --- /dev/null +++ b/src/Workspaces/Core/Portable/CodeRefactorings/FixAllOccurences/FixAllLogger.cs @@ -0,0 +1,120 @@ +// 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.Collections.Immutable; +using System.Linq; +using Microsoft.CodeAnalysis.Internal.Log; +using Roslyn.Utilities; + +namespace Microsoft.CodeAnalysis.CodeRefactorings +{ + /// + /// Fix all occurrences logging. + /// + internal static class FixAllLogger + { + // correlation id of all events related to same instance of fix all + public const string CorrelationId = nameof(CorrelationId); + + // Fix all context logging. + private const string CodeRefactoringProvider = nameof(CodeRefactoringProvider); + private const string CodeActionEquivalenceKey = nameof(CodeActionEquivalenceKey); + public const string FixAllScope = nameof(FixAllScope); + private const string LanguageName = nameof(LanguageName); + private const string DocumentCount = nameof(DocumentCount); + + // Fix all computation result logging. + private const string Result = nameof(Result); + private const string Completed = nameof(Completed); + private const string TimedOut = nameof(TimedOut); + private const string Cancelled = nameof(Cancelled); + private const string AllChangesApplied = nameof(AllChangesApplied); + private const string SubsetOfChangesApplied = nameof(SubsetOfChangesApplied); + + public static void LogState(FixAllState fixAllState, bool isInternalCodeFixProvider) + { + Logger.Log(FunctionId.CodeFixes_FixAllOccurrencesContext, KeyValueLogMessage.Create(m => + { + m[CorrelationId] = fixAllState.CorrelationId; + + if (isInternalCodeFixProvider) + { + m[CodeRefactoringProvider] = fixAllState.CodeRefactoringProvider.GetType().FullName!; + m[CodeActionEquivalenceKey] = fixAllState.CodeAction.EquivalenceKey; + m[LanguageName] = fixAllState.Project.Language; + } + else + { + m[CodeRefactoringProvider] = fixAllState.CodeRefactoringProvider.GetType().FullName!.GetHashCode().ToString(); + m[CodeActionEquivalenceKey] = fixAllState.CodeAction.EquivalenceKey?.GetHashCode().ToString(); + m[LanguageName] = fixAllState.Project.Language.GetHashCode().ToString(); + } + + m[FixAllScope] = fixAllState.FixAllScope.ToString(); + switch (fixAllState.FixAllScope) + { + case CodeRefactorings.FixAllScope.Project: + m[DocumentCount] = fixAllState.Project.DocumentIds.Count; + break; + + case CodeRefactorings.FixAllScope.Solution: + m[DocumentCount] = fixAllState.Project.Solution.Projects.Sum(p => p.DocumentIds.Count); + break; + } + })); + } + + public static void LogComputationResult(int correlationId, bool completed, bool timedOut = false) + { + Contract.ThrowIfTrue(completed && timedOut); + + string value; + if (completed) + { + value = Completed; + } + else if (timedOut) + { + value = TimedOut; + } + else + { + value = Cancelled; + } + + Logger.Log(FunctionId.Refactoring_FixAllOccurrencesComputation, KeyValueLogMessage.Create(m => + { + m[CorrelationId] = correlationId; + m[Result] = value; + })); + } + + public static void LogPreviewChangesResult(int? correlationId, bool applied, bool allChangesApplied = true) + { + string value; + if (applied) + { + value = allChangesApplied ? AllChangesApplied : SubsetOfChangesApplied; + } + else + { + value = Cancelled; + } + + Logger.Log(FunctionId.Refactoring_FixAllOccurrencesPreviewChanges, KeyValueLogMessage.Create(m => + { + // we might not have this info for suppression + if (correlationId.HasValue) + { + m[CorrelationId] = correlationId; + } + + m[Result] = value; + })); + } + + public static LogMessage CreateCorrelationLogMessage(int correlationId) + => KeyValueLogMessage.Create(LogType.UserAction, m => m[CorrelationId] = correlationId); + } +} diff --git a/src/Workspaces/Core/Portable/CodeRefactorings/FixAllOccurences/FixAllProvider.cs b/src/Workspaces/Core/Portable/CodeRefactorings/FixAllOccurences/FixAllProvider.cs new file mode 100644 index 0000000000000..8bc6c6a226439 --- /dev/null +++ b/src/Workspaces/Core/Portable/CodeRefactorings/FixAllOccurences/FixAllProvider.cs @@ -0,0 +1,88 @@ +// 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 System.Collections.Generic; +using System.Collections.Immutable; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.CodeActions; +using Microsoft.CodeAnalysis.Text; +using Roslyn.Utilities; + +namespace Microsoft.CodeAnalysis.CodeRefactorings +{ + /// + /// Implement this abstract type to provide fix all occurrences support for code refactorings. + /// + public abstract class FixAllProvider + { + /// + /// Gets the supported scopes for applying multiple occurrences of a code refactoring. + /// By default, it returns the following scopes: + /// (a) + /// (b) and + /// (c) + /// + public virtual IEnumerable GetSupportedFixAllScopes() + => ImmutableArray.Create(FixAllScope.Document, FixAllScope.Project, FixAllScope.Solution); + + /// + /// Gets fix all occurrences fix for the given fixAllContext. + /// + public abstract Task GetFixAsync(FixAllContext fixAllContext); + + /// + /// Create a that fixes documents independently. + /// This can be used in the case where refactoring(s) registered by this provider + /// only affect a single . + /// + /// + /// Callback that will apply the refactorings present in the provided document. The document returned will only be + /// examined for its content (e.g. it's or . No other aspects + /// of it (like attributes), or changes to the or it points at + /// will be considered. + /// + /// Indicates if is supported or not. + /// Indicates if is supported or not. + /// Indicates if is supported or not. + public static FixAllProvider Create( + Func> fixAllAsync, + bool supportsFixAllForSelection, + bool supportsFixAllForContainingMember, + bool supportsFixAllForContainingType) + { + if (fixAllAsync == null) + throw new ArgumentNullException(nameof(fixAllAsync)); + + return new CallbackDocumentBasedFixAllProvider(fixAllAsync, + supportsFixAllForSelection, supportsFixAllForContainingMember, supportsFixAllForContainingType); + } + + private sealed class CallbackDocumentBasedFixAllProvider : DocumentBasedFixAllProvider + { + private readonly Func> _fixAllAsync; + + public CallbackDocumentBasedFixAllProvider( + Func> fixAllAsync, + bool supportsFixAllForSelection, + bool supportsFixAllForContainingMember, + bool supportsFixAllForContainingType) + { + _fixAllAsync = fixAllAsync; + SupportsFixAllForSelection = supportsFixAllForSelection; + SupportsFixAllForContainingMember = supportsFixAllForContainingMember; + SupportsFixAllForContainingType = supportsFixAllForContainingType; + } + + protected override bool SupportsFixAllForSelection { get; } + + protected override bool SupportsFixAllForContainingMember { get; } + + protected override bool SupportsFixAllForContainingType { get; } + + protected override Task FixAllAsync(FixAllContext context) + => _fixAllAsync(context); + } + } +} diff --git a/src/Workspaces/Core/Portable/CodeRefactorings/FixAllOccurences/FixAllProviderInfo.cs b/src/Workspaces/Core/Portable/CodeRefactorings/FixAllOccurences/FixAllProviderInfo.cs new file mode 100644 index 0000000000000..71ea7d734151b --- /dev/null +++ b/src/Workspaces/Core/Portable/CodeRefactorings/FixAllOccurences/FixAllProviderInfo.cs @@ -0,0 +1,48 @@ +// 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 System.Collections.Generic; +using System.Collections.Immutable; +using Roslyn.Utilities; + +namespace Microsoft.CodeAnalysis.CodeRefactorings +{ + /// + /// Contains computed information specific to FixAll for a given , such as supported s. + /// + internal sealed class FixAllProviderInfo + { + public readonly FixAllProvider FixAllProvider; + public readonly ImmutableArray SupportedScopes; + + private FixAllProviderInfo( + FixAllProvider fixAllProvider, + ImmutableArray supportedScopes) + { + FixAllProvider = fixAllProvider; + SupportedScopes = supportedScopes; + } + + /// + /// Gets an optional for the given code refactoring provider. + /// + public static FixAllProviderInfo? Create(CodeRefactoringProvider provider) + { + var fixAllProvider = provider.GetFixAllProvider(); + if (fixAllProvider == null) + { + return null; + } + + var scopes = fixAllProvider.GetSupportedFixAllScopes().ToImmutableArrayOrEmpty(); + if (scopes.IsEmpty) + { + return null; + } + + return new FixAllProviderInfo(fixAllProvider, scopes); + } + } +} diff --git a/src/Workspaces/Core/Portable/CodeRefactorings/FixAllOccurences/FixAllScope.cs b/src/Workspaces/Core/Portable/CodeRefactorings/FixAllOccurences/FixAllScope.cs new file mode 100644 index 0000000000000..bc7d71bcdfe65 --- /dev/null +++ b/src/Workspaces/Core/Portable/CodeRefactorings/FixAllOccurences/FixAllScope.cs @@ -0,0 +1,21 @@ +// 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. + +namespace Microsoft.CodeAnalysis.CodeRefactorings +{ + /// + /// Indicates scope for "Fix all occurrences" for code refactorings provided by each . + /// + public enum FixAllScope + { + Document, + Project, + Solution, + Selection, + ContainingMember, + ContainingType, + + Custom = int.MaxValue + } +} diff --git a/src/Workspaces/Core/Portable/FixAll/CommonDocumentBasedFixAllProviderHelpers.cs b/src/Workspaces/Core/Portable/FixAll/CommonDocumentBasedFixAllProviderHelpers.cs new file mode 100644 index 0000000000000..17cef790e9b88 --- /dev/null +++ b/src/Workspaces/Core/Portable/FixAll/CommonDocumentBasedFixAllProviderHelpers.cs @@ -0,0 +1,97 @@ +// 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.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.CodeActions; +using Microsoft.CodeAnalysis.PooledObjects; +using Microsoft.CodeAnalysis.Shared.Extensions; +using Microsoft.CodeAnalysis.Shared.Utilities; +using Microsoft.CodeAnalysis.Text; +using Roslyn.Utilities; + +namespace Microsoft.CodeAnalysis.FixAll +{ + /// + /// Helper methods for DocumentBasedFixAllProvider common to code fixes and refactorings. + /// + internal static class CommonDocumentBasedFixAllProviderHelpers + { + /// + /// Take all the fixed documents and format/simplify/clean them up (if the language supports that), and take the + /// resultant text and apply it to the solution. If the language doesn't support cleanup, then just take the + /// given text and apply that instead. + /// + internal static async Task CleanupAndApplyChangesAsync( + IProgressTracker progressTracker, + Solution currentSolution, + Dictionary docIdToNewRootOrText, + CancellationToken cancellationToken) + { + using var _1 = progressTracker.ItemCompletedScope(); + + if (docIdToNewRootOrText.Count > 0) + { + // Next, go and insert those all into the solution so all the docs in this particular project point at + // the new trees (or text). At this point though, the trees have not been cleaned up. We don't cleanup + // the documents as they are created, or one at a time as we add them, as that would cause us to run + // cleanup on N different solution forks (which would be very expensive). Instead, by adding all the + // changed documents to one solution, and hten cleaning *those* we only perform cleanup semantics on one + // forked solution. + foreach (var (docId, (newRoot, newText)) in docIdToNewRootOrText) + { + currentSolution = newRoot != null + ? currentSolution.WithDocumentSyntaxRoot(docId, newRoot) + : currentSolution.WithDocumentText(docId, newText!); + } + + // Next, go and cleanup any trees we inserted. Once we clean the document, we get the text of it and + // insert that back into the final solution. This way we can release both the original fixed tree, and + // the cleaned tree (both of which can be much more expensive than just text). + // + // Do this in parallel across all the documents that were fixed. + using var _2 = ArrayBuilder>.GetInstance(out var tasks); + + foreach (var (docId, (newRoot, _)) in docIdToNewRootOrText) + { + if (newRoot != null) + { + var dirtyDocument = currentSolution.GetRequiredDocument(docId); + tasks.Add(Task.Run(async () => + { + var cleanedDocument = await PostProcessCodeAction.Instance.PostProcessChangesAsync(dirtyDocument, cancellationToken).ConfigureAwait(false); + var cleanedText = await cleanedDocument.GetTextAsync(cancellationToken).ConfigureAwait(false); + return (dirtyDocument.Id, cleanedText); + }, cancellationToken)); + } + } + + await Task.WhenAll(tasks).ConfigureAwait(false); + + // Finally, apply the cleaned documents to the solution. + foreach (var task in tasks) + { + var (docId, cleanedText) = await task.ConfigureAwait(false); + currentSolution = currentSolution.WithDocumentText(docId, cleanedText); + } + } + + return currentSolution; + } + + /// + /// Dummy class just to get access to + /// + private class PostProcessCodeAction : CodeAction + { + public static readonly PostProcessCodeAction Instance = new(); + + public override string Title => ""; + + public new Task PostProcessChangesAsync(Document document, CancellationToken cancellationToken) + => base.PostProcessChangesAsync(document, cancellationToken); + } + } +} diff --git a/src/Workspaces/Core/Portable/PublicAPI.Unshipped.txt b/src/Workspaces/Core/Portable/PublicAPI.Unshipped.txt index 2bada5d42e0b8..e88f491a1c7af 100644 --- a/src/Workspaces/Core/Portable/PublicAPI.Unshipped.txt +++ b/src/Workspaces/Core/Portable/PublicAPI.Unshipped.txt @@ -1,5 +1,33 @@ +abstract Microsoft.CodeAnalysis.CodeRefactorings.DocumentBasedFixAllProvider.FixAllAsync(Microsoft.CodeAnalysis.CodeRefactorings.FixAllContext fixAllContext) -> System.Threading.Tasks.Task +abstract Microsoft.CodeAnalysis.CodeRefactorings.DocumentBasedFixAllProvider.SupportsFixAllForContainingMember.get -> bool +abstract Microsoft.CodeAnalysis.CodeRefactorings.DocumentBasedFixAllProvider.SupportsFixAllForContainingType.get -> bool +abstract Microsoft.CodeAnalysis.CodeRefactorings.DocumentBasedFixAllProvider.SupportsFixAllForSelection.get -> bool +abstract Microsoft.CodeAnalysis.CodeRefactorings.FixAllProvider.GetFixAsync(Microsoft.CodeAnalysis.CodeRefactorings.FixAllContext fixAllContext) -> System.Threading.Tasks.Task +Microsoft.CodeAnalysis.CodeRefactorings.DocumentBasedFixAllProvider +Microsoft.CodeAnalysis.CodeRefactorings.DocumentBasedFixAllProvider.DocumentBasedFixAllProvider() -> void +Microsoft.CodeAnalysis.CodeRefactorings.FixAllContext +Microsoft.CodeAnalysis.CodeRefactorings.FixAllContext.CancellationToken.get -> System.Threading.CancellationToken +Microsoft.CodeAnalysis.CodeRefactorings.FixAllContext.CodeAction.get -> Microsoft.CodeAnalysis.CodeActions.CodeAction +Microsoft.CodeAnalysis.CodeRefactorings.FixAllContext.CodeRefactoringProvider.get -> Microsoft.CodeAnalysis.CodeRefactorings.CodeRefactoringProvider +Microsoft.CodeAnalysis.CodeRefactorings.FixAllContext.Document.get -> Microsoft.CodeAnalysis.Document +Microsoft.CodeAnalysis.CodeRefactorings.FixAllContext.FixAllSpan.get -> Microsoft.CodeAnalysis.Text.TextSpan? +Microsoft.CodeAnalysis.CodeRefactorings.FixAllContext.Scope.get -> Microsoft.CodeAnalysis.CodeRefactorings.FixAllScope +Microsoft.CodeAnalysis.CodeRefactorings.FixAllContext.WithCancellationToken(System.Threading.CancellationToken cancellationToken) -> Microsoft.CodeAnalysis.CodeRefactorings.FixAllContext +Microsoft.CodeAnalysis.CodeRefactorings.FixAllProvider +Microsoft.CodeAnalysis.CodeRefactorings.FixAllProvider.FixAllProvider() -> void +Microsoft.CodeAnalysis.CodeRefactorings.FixAllScope +Microsoft.CodeAnalysis.CodeRefactorings.FixAllScope.ContainingMember = 4 -> Microsoft.CodeAnalysis.CodeRefactorings.FixAllScope +Microsoft.CodeAnalysis.CodeRefactorings.FixAllScope.ContainingType = 5 -> Microsoft.CodeAnalysis.CodeRefactorings.FixAllScope +Microsoft.CodeAnalysis.CodeRefactorings.FixAllScope.Custom = 2147483647 -> Microsoft.CodeAnalysis.CodeRefactorings.FixAllScope +Microsoft.CodeAnalysis.CodeRefactorings.FixAllScope.Document = 0 -> Microsoft.CodeAnalysis.CodeRefactorings.FixAllScope +Microsoft.CodeAnalysis.CodeRefactorings.FixAllScope.Project = 1 -> Microsoft.CodeAnalysis.CodeRefactorings.FixAllScope +Microsoft.CodeAnalysis.CodeRefactorings.FixAllScope.Selection = 3 -> Microsoft.CodeAnalysis.CodeRefactorings.FixAllScope +Microsoft.CodeAnalysis.CodeRefactorings.FixAllScope.Solution = 2 -> Microsoft.CodeAnalysis.CodeRefactorings.FixAllScope Microsoft.CodeAnalysis.Editing.SyntaxEditor.SyntaxEditor(Microsoft.CodeAnalysis.SyntaxNode root, Microsoft.CodeAnalysis.Host.HostWorkspaceServices services) -> void *REMOVED*static Microsoft.CodeAnalysis.Editing.SyntaxGenerator.DefaultRemoveOptions -> Microsoft.CodeAnalysis.SyntaxRemoveOptions +override sealed Microsoft.CodeAnalysis.CodeRefactorings.DocumentBasedFixAllProvider.GetFixAsync(Microsoft.CodeAnalysis.CodeRefactorings.FixAllContext fixAllContext) -> System.Threading.Tasks.Task +override sealed Microsoft.CodeAnalysis.CodeRefactorings.DocumentBasedFixAllProvider.GetSupportedFixAllScopes() -> System.Collections.Generic.IEnumerable +static Microsoft.CodeAnalysis.CodeRefactorings.FixAllProvider.Create(System.Func> fixAllAsync, bool supportsFixAllForSelection, bool supportsFixAllForContainingMember, bool supportsFixAllForContainingType) -> Microsoft.CodeAnalysis.CodeRefactorings.FixAllProvider static readonly Microsoft.CodeAnalysis.Editing.SyntaxGenerator.DefaultRemoveOptions -> Microsoft.CodeAnalysis.SyntaxRemoveOptions Microsoft.CodeAnalysis.Rename.SymbolRenameOptions Microsoft.CodeAnalysis.Rename.SymbolRenameOptions.SymbolRenameOptions() -> void @@ -21,3 +49,5 @@ Microsoft.CodeAnalysis.Rename.DocumentRenameOptions.RenameMatchingTypeInStrings. Microsoft.CodeAnalysis.Rename.DocumentRenameOptions.RenameMatchingTypeInComments.init -> void static Microsoft.CodeAnalysis.Rename.Renamer.RenameDocumentAsync(Microsoft.CodeAnalysis.Document document, Microsoft.CodeAnalysis.Rename.DocumentRenameOptions options, string newDocumentName, System.Collections.Generic.IReadOnlyList newDocumentFolders = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task static Microsoft.CodeAnalysis.Rename.Renamer.RenameSymbolAsync(Microsoft.CodeAnalysis.Solution solution, Microsoft.CodeAnalysis.ISymbol symbol, Microsoft.CodeAnalysis.Rename.SymbolRenameOptions options, string newName, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task +virtual Microsoft.CodeAnalysis.CodeRefactorings.DocumentBasedFixAllProvider.GetFixAllTitle(Microsoft.CodeAnalysis.CodeRefactorings.FixAllContext fixAllContext) -> string +virtual Microsoft.CodeAnalysis.CodeRefactorings.FixAllProvider.GetSupportedFixAllScopes() -> System.Collections.Generic.IEnumerable diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Log/FunctionId.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Log/FunctionId.cs index 20787130a313c..82fd79a4fe35a 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Log/FunctionId.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Log/FunctionId.cs @@ -539,5 +539,10 @@ internal enum FunctionId NavigateToExternalSources = 531, StackTraceToolWindow_ShowOnActivated = 540, + + Refactoring_FixAllOccurrencesSession = 550, + Refactoring_FixAllOccurrencesContext = 551, + Refactoring_FixAllOccurrencesComputation = 552, + Refactoring_FixAllOccurrencesPreviewChanges = 553, } } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeRefactorings/FixAll/FixAllContextExtensions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeRefactorings/FixAll/FixAllContextExtensions.cs new file mode 100644 index 0000000000000..50439b07ed1f2 --- /dev/null +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeRefactorings/FixAll/FixAllContextExtensions.cs @@ -0,0 +1,22 @@ +// 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.CodeActions; +using Microsoft.CodeAnalysis.Shared.Utilities; +using Roslyn.Utilities; + +namespace Microsoft.CodeAnalysis.CodeRefactorings +{ + internal static class FixAllContextExtensions + { + public static IProgressTracker GetProgressTracker(this FixAllContext context) + { +#if CODE_STYLE + return NoOpProgressTracker.Instance; +#else + return context.ProgressTracker; +#endif + } + } +} diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeRefactorings/FixAll/FixAllState.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeRefactorings/FixAll/FixAllState.cs new file mode 100644 index 0000000000000..764971374118e --- /dev/null +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeRefactorings/FixAll/FixAllState.cs @@ -0,0 +1,115 @@ +// 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.CodeActions; +using Microsoft.CodeAnalysis.Internal.Log; +using Microsoft.CodeAnalysis.Text; +using Roslyn.Utilities; + +namespace Microsoft.CodeAnalysis.CodeRefactorings +{ + internal partial class FixAllState + { + internal readonly int CorrelationId = LogAggregator.GetNextId(); + + public FixAllProvider? FixAllProvider { get; } + public CodeAction CodeAction { get; } + public CodeRefactoringProvider CodeRefactoringProvider { get; } + public Document? Document { get; } + public Project Project { get; } + public FixAllScope FixAllScope { get; } + public TextSpan? FixAllSpan { get; } + public Solution Solution => this.Project.Solution; + + internal FixAllState( + FixAllProvider? fixAllProvider, + Document document, + CodeRefactoringProvider codeRefactoringProvider, + FixAllScope fixAllScope, + TextSpan? fixAllSpan, + CodeAction codeAction) + : this(fixAllProvider, document, document.Project, codeRefactoringProvider, fixAllScope, fixAllSpan, codeAction) + { + if (document == null) + { + throw new ArgumentNullException(nameof(document)); + } + } + + internal FixAllState( + FixAllProvider? fixAllProvider, + Project project, + CodeRefactoringProvider codeRefactoringProvider, + FixAllScope fixAllScope, + CodeAction codeAction) + : this(fixAllProvider, document: null, project, codeRefactoringProvider, fixAllScope, fixAllSpan: null, codeAction) + { + if (project == null) + { + throw new ArgumentNullException(nameof(project)); + } + } + + private FixAllState( + FixAllProvider? fixAllProvider, + Document? document, + Project project, + CodeRefactoringProvider codeRefactoringProvider, + FixAllScope fixAllScope, + TextSpan? fixAllSpan, + CodeAction codeAction) + { + Contract.ThrowIfNull(project); + Contract.ThrowIfFalse(!fixAllSpan.HasValue || fixAllScope is FixAllScope.Document or + FixAllScope.Selection or FixAllScope.ContainingMember or FixAllScope.ContainingType); + + this.FixAllProvider = fixAllProvider; + this.Document = document; + this.Project = project; + this.CodeRefactoringProvider = codeRefactoringProvider ?? throw new ArgumentNullException(nameof(codeRefactoringProvider)); + this.FixAllScope = fixAllScope; + this.FixAllSpan = fixAllSpan; + this.CodeAction = codeAction; + } + + public FixAllState WithDocument(Document? document) + => this.With(document: document); + + public FixAllState WithProject(Project project) + => this.With(project: project, fixAllSpan: null); + + public FixAllState WithScope(FixAllScope scope) + => this.With(scope: scope); + + public FixAllState With( + Optional document = default, + Optional project = default, + Optional scope = default, + Optional fixAllSpan = default) + { + var newDocument = document.HasValue ? document.Value : this.Document; + var newProject = project.HasValue ? project.Value : this.Project; + var newFixAllScope = scope.HasValue ? scope.Value : this.FixAllScope; + var newFixAllSpan = fixAllSpan.HasValue ? fixAllSpan.Value : this.FixAllSpan; + + if (newDocument == this.Document && + newProject == this.Project && + newFixAllScope == this.FixAllScope && + newFixAllSpan == this.FixAllSpan) + { + return this; + } + + return new FixAllState( + this.FixAllProvider, + newDocument, + newProject, + this.CodeRefactoringProvider, + newFixAllScope, + newFixAllSpan, + this.CodeAction); + } + } +} diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeRefactorings/SyntaxEditorBasedCodeRefactoringProvider.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeRefactorings/SyntaxEditorBasedCodeRefactoringProvider.cs new file mode 100644 index 0000000000000..cb236fb281c6a --- /dev/null +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeRefactorings/SyntaxEditorBasedCodeRefactoringProvider.cs @@ -0,0 +1,84 @@ +// 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 System.Collections.Immutable; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.CodeActions; +using Microsoft.CodeAnalysis.Editing; +using Microsoft.CodeAnalysis.Shared.Extensions; +using Microsoft.CodeAnalysis.Text; + +namespace Microsoft.CodeAnalysis.CodeRefactorings +{ + internal abstract partial class SyntaxEditorBasedCodeRefactoringProvider : CodeRefactoringProvider + { + private readonly bool _supportsFixAll; + private readonly bool _supportsFixAllForSelection; + private readonly bool _supportsFixAllForContainingMember; + private readonly bool _supportsFixAllForContainingType; + + protected SyntaxEditorBasedCodeRefactoringProvider( + bool supportsFixAll = true, + bool supportsFixAllForSelection = true, + bool supportsFixAllForContainingMember = true, + bool supportsFixAllForContainingType = true) + { + _supportsFixAll = supportsFixAll; + _supportsFixAllForSelection = supportsFixAllForSelection; + _supportsFixAllForContainingMember = supportsFixAllForContainingMember; + _supportsFixAllForContainingType = supportsFixAllForContainingType; + } + + + public sealed override FixAllProvider? GetFixAllProvider() + { + if (!_supportsFixAll) + return null; + + return FixAllProvider.Create( + async fixAllContext => + { + return await this.FixAllAsync(fixAllContext.Document, fixAllContext.FixAllSpan, fixAllContext.CodeAction, fixAllContext.CancellationToken).ConfigureAwait(false); + }, + _supportsFixAllForSelection, + _supportsFixAllForContainingMember, + _supportsFixAllForContainingType); + } + + protected Task FixAsync( + Document document, TextSpan fixAllSpan, CancellationToken cancellationToken) + { + return FixAllWithEditorAsync(document, + editor => FixAllAsync(document, fixAllSpan, originalCodeAction: null, editor, cancellationToken), + cancellationToken); + } + + protected Task FixAllAsync( + Document document, TextSpan? fixAllSpan, CodeAction originalCodeAction, CancellationToken cancellationToken) + { + return FixAllWithEditorAsync(document, + editor => FixAllAsync(document, fixAllSpan ?? editor.OriginalRoot.FullSpan, originalCodeAction, editor, cancellationToken), + cancellationToken); + } + + internal static async Task FixAllWithEditorAsync( + Document document, + Func editAsync, + CancellationToken cancellationToken) + { + var root = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); + var editor = new SyntaxEditor(root, document.Project.Solution.Workspace.Services); + + await editAsync(editor).ConfigureAwait(false); + + var newRoot = editor.GetChangedRoot(); + return document.WithSyntaxRoot(newRoot); + } + + protected abstract Task FixAllAsync( + Document document, TextSpan fixAllSpan, CodeAction? originalCodeAction, SyntaxEditor editor, CancellationToken cancellationToken); + } +} diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/WorkspaceExtensions.projitems b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/WorkspaceExtensions.projitems index fd07f372126b2..83dd187b87bc2 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/WorkspaceExtensions.projitems +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/WorkspaceExtensions.projitems @@ -10,6 +10,9 @@ + + + @@ -90,7 +93,9 @@ - + + Designer + diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/WorkspaceExtensionsResources.resx b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/WorkspaceExtensionsResources.resx index f5b438da33afe..c8b7d23a9c9b6 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/WorkspaceExtensionsResources.resx +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/WorkspaceExtensionsResources.resx @@ -126,6 +126,15 @@ Fix all '{0}' in '{1}' + + Fix all '{0}' in selection for '{1}' + + + Fix all '{0}' in containing member for '{1}' + + + Fix all '{0}' in containing type for '{1}' + Fix all '{0}' in Solution diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/xlf/WorkspaceExtensionsResources.cs.xlf b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/xlf/WorkspaceExtensionsResources.cs.xlf index b260a21696b04..cbdb98c5a8a6e 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/xlf/WorkspaceExtensionsResources.cs.xlf +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/xlf/WorkspaceExtensionsResources.cs.xlf @@ -22,6 +22,21 @@ Opravit vše ({0}) v řešení + + Fix all '{0}' in containing member for '{1}' + Fix all '{0}' in containing member for '{1}' + + + + Fix all '{0}' in containing type for '{1}' + Fix all '{0}' in containing type for '{1}' + + + + Fix all '{0}' in selection for '{1}' + Fix all '{0}' in selection for '{1}' + + Project of ID {0} is required to accomplish the task but is not available from the solution Ke splnění úkolu se vyžaduje projekt s ID {0}, který ale není z řešení dostupný. diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/xlf/WorkspaceExtensionsResources.de.xlf b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/xlf/WorkspaceExtensionsResources.de.xlf index e1b4b7ed01fc5..382d94f89030d 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/xlf/WorkspaceExtensionsResources.de.xlf +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/xlf/WorkspaceExtensionsResources.de.xlf @@ -22,6 +22,21 @@ Alle '{0}' in Lösung reparieren + + Fix all '{0}' in containing member for '{1}' + Fix all '{0}' in containing member for '{1}' + + + + Fix all '{0}' in containing type for '{1}' + Fix all '{0}' in containing type for '{1}' + + + + Fix all '{0}' in selection for '{1}' + Fix all '{0}' in selection for '{1}' + + Project of ID {0} is required to accomplish the task but is not available from the solution Ein Projekt mit der ID "{0}" ist zum Ausführen der Aufgabe erforderlich, steht aber in der Projektmappe nicht zur Verfügung. diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/xlf/WorkspaceExtensionsResources.es.xlf b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/xlf/WorkspaceExtensionsResources.es.xlf index 1b78e6d680b8a..956806d04b482 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/xlf/WorkspaceExtensionsResources.es.xlf +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/xlf/WorkspaceExtensionsResources.es.xlf @@ -22,6 +22,21 @@ Corregir todo '{0}' en solución + + Fix all '{0}' in containing member for '{1}' + Fix all '{0}' in containing member for '{1}' + + + + Fix all '{0}' in containing type for '{1}' + Fix all '{0}' in containing type for '{1}' + + + + Fix all '{0}' in selection for '{1}' + Fix all '{0}' in selection for '{1}' + + Project of ID {0} is required to accomplish the task but is not available from the solution Se necesita el identificador de proyecto "{0}" para realizar la tarea, pero no está disponibles en la solución diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/xlf/WorkspaceExtensionsResources.fr.xlf b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/xlf/WorkspaceExtensionsResources.fr.xlf index c00a9f0c92b08..0a7c142d774be 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/xlf/WorkspaceExtensionsResources.fr.xlf +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/xlf/WorkspaceExtensionsResources.fr.xlf @@ -22,6 +22,21 @@ Corriger tous les '{0}' dans la solution + + Fix all '{0}' in containing member for '{1}' + Fix all '{0}' in containing member for '{1}' + + + + Fix all '{0}' in containing type for '{1}' + Fix all '{0}' in containing type for '{1}' + + + + Fix all '{0}' in selection for '{1}' + Fix all '{0}' in selection for '{1}' + + Project of ID {0} is required to accomplish the task but is not available from the solution Le projet de l'ID {0} est nécessaire pour accomplir la tâche mais n'est pas disponible dans la solution diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/xlf/WorkspaceExtensionsResources.it.xlf b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/xlf/WorkspaceExtensionsResources.it.xlf index 584ccef5f4fb7..e7323a9c832ec 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/xlf/WorkspaceExtensionsResources.it.xlf +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/xlf/WorkspaceExtensionsResources.it.xlf @@ -22,6 +22,21 @@ Correggi tutti '{0}' nella soluzione + + Fix all '{0}' in containing member for '{1}' + Fix all '{0}' in containing member for '{1}' + + + + Fix all '{0}' in containing type for '{1}' + Fix all '{0}' in containing type for '{1}' + + + + Fix all '{0}' in selection for '{1}' + Fix all '{0}' in selection for '{1}' + + Project of ID {0} is required to accomplish the task but is not available from the solution Per eseguire l'attività, è necessario il progetto con ID '{0}', che però non è disponibile dalla soluzione diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/xlf/WorkspaceExtensionsResources.ja.xlf b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/xlf/WorkspaceExtensionsResources.ja.xlf index 878a0817f7cb4..c35ae4f68de8e 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/xlf/WorkspaceExtensionsResources.ja.xlf +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/xlf/WorkspaceExtensionsResources.ja.xlf @@ -22,6 +22,21 @@ ソリューションに含まれているすべての '{0}' を修正します + + Fix all '{0}' in containing member for '{1}' + Fix all '{0}' in containing member for '{1}' + + + + Fix all '{0}' in containing type for '{1}' + Fix all '{0}' in containing type for '{1}' + + + + Fix all '{0}' in selection for '{1}' + Fix all '{0}' in selection for '{1}' + + Project of ID {0} is required to accomplish the task but is not available from the solution タスクの完了には ID {0} のプロジェクトが必要ですが、このソリューションからは利用できません diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/xlf/WorkspaceExtensionsResources.ko.xlf b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/xlf/WorkspaceExtensionsResources.ko.xlf index b7893a187a979..c1e2981d2e56a 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/xlf/WorkspaceExtensionsResources.ko.xlf +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/xlf/WorkspaceExtensionsResources.ko.xlf @@ -22,6 +22,21 @@ 솔루션의 모든 '{0}' 수정 + + Fix all '{0}' in containing member for '{1}' + Fix all '{0}' in containing member for '{1}' + + + + Fix all '{0}' in containing type for '{1}' + Fix all '{0}' in containing type for '{1}' + + + + Fix all '{0}' in selection for '{1}' + Fix all '{0}' in selection for '{1}' + + Project of ID {0} is required to accomplish the task but is not available from the solution 작업을 수행하는 데 ID가 {0}인 프로젝트가 필요하지만, 솔루션에서 해당 프로젝트를 사용할 수 없습니다. diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/xlf/WorkspaceExtensionsResources.pl.xlf b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/xlf/WorkspaceExtensionsResources.pl.xlf index 6ce41179340ce..80f409625155c 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/xlf/WorkspaceExtensionsResources.pl.xlf +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/xlf/WorkspaceExtensionsResources.pl.xlf @@ -22,6 +22,21 @@ Napraw wszystkie wystąpienia elementu „{0}” w rozwiązaniu + + Fix all '{0}' in containing member for '{1}' + Fix all '{0}' in containing member for '{1}' + + + + Fix all '{0}' in containing type for '{1}' + Fix all '{0}' in containing type for '{1}' + + + + Fix all '{0}' in selection for '{1}' + Fix all '{0}' in selection for '{1}' + + Project of ID {0} is required to accomplish the task but is not available from the solution Do wykonania zadania wymagany jest projekt o identyfikatorze {0}, ale nie jest on udostępniany przez rozwiązanie diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/xlf/WorkspaceExtensionsResources.pt-BR.xlf b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/xlf/WorkspaceExtensionsResources.pt-BR.xlf index d9f762bf4227e..bec57cd2e0372 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/xlf/WorkspaceExtensionsResources.pt-BR.xlf +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/xlf/WorkspaceExtensionsResources.pt-BR.xlf @@ -22,6 +22,21 @@ Corrigir todos os '{0}' na Solução + + Fix all '{0}' in containing member for '{1}' + Fix all '{0}' in containing member for '{1}' + + + + Fix all '{0}' in containing type for '{1}' + Fix all '{0}' in containing type for '{1}' + + + + Fix all '{0}' in selection for '{1}' + Fix all '{0}' in selection for '{1}' + + Project of ID {0} is required to accomplish the task but is not available from the solution O projeto com a ID {0} é necessário para realizar a tarefa, mas não está disponível na solução diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/xlf/WorkspaceExtensionsResources.ru.xlf b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/xlf/WorkspaceExtensionsResources.ru.xlf index 742117c1c9930..46441e3ebd86e 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/xlf/WorkspaceExtensionsResources.ru.xlf +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/xlf/WorkspaceExtensionsResources.ru.xlf @@ -22,6 +22,21 @@ Исправить все "{0}" в решении + + Fix all '{0}' in containing member for '{1}' + Fix all '{0}' in containing member for '{1}' + + + + Fix all '{0}' in containing type for '{1}' + Fix all '{0}' in containing type for '{1}' + + + + Fix all '{0}' in selection for '{1}' + Fix all '{0}' in selection for '{1}' + + Project of ID {0} is required to accomplish the task but is not available from the solution Проект с ИД "{0}" необходим для выполнения задачи, но он недоступен из решения. diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/xlf/WorkspaceExtensionsResources.tr.xlf b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/xlf/WorkspaceExtensionsResources.tr.xlf index befe4cda1ace2..36f5036add82f 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/xlf/WorkspaceExtensionsResources.tr.xlf +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/xlf/WorkspaceExtensionsResources.tr.xlf @@ -22,6 +22,21 @@ Çözüm'de geçtiği her yerde '{0}' ifadesini düzelt + + Fix all '{0}' in containing member for '{1}' + Fix all '{0}' in containing member for '{1}' + + + + Fix all '{0}' in containing type for '{1}' + Fix all '{0}' in containing type for '{1}' + + + + Fix all '{0}' in selection for '{1}' + Fix all '{0}' in selection for '{1}' + + Project of ID {0} is required to accomplish the task but is not available from the solution Görevi gerçekleştirmek için '{0}' kimlikli proje gerekli, ancak bu proje çözümde yok diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/xlf/WorkspaceExtensionsResources.zh-Hans.xlf b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/xlf/WorkspaceExtensionsResources.zh-Hans.xlf index 6196afb3309a9..f6b4e1d09f57f 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/xlf/WorkspaceExtensionsResources.zh-Hans.xlf +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/xlf/WorkspaceExtensionsResources.zh-Hans.xlf @@ -22,6 +22,21 @@ 修复解决方案中的所有“{0}” + + Fix all '{0}' in containing member for '{1}' + Fix all '{0}' in containing member for '{1}' + + + + Fix all '{0}' in containing type for '{1}' + Fix all '{0}' in containing type for '{1}' + + + + Fix all '{0}' in selection for '{1}' + Fix all '{0}' in selection for '{1}' + + Project of ID {0} is required to accomplish the task but is not available from the solution 需要 ID 为 {0} 的项目才能完成任务,但无法从解决方案中使用该项目 diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/xlf/WorkspaceExtensionsResources.zh-Hant.xlf b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/xlf/WorkspaceExtensionsResources.zh-Hant.xlf index 726bc7c6fa2ab..7878b34c76b47 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/xlf/WorkspaceExtensionsResources.zh-Hant.xlf +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/xlf/WorkspaceExtensionsResources.zh-Hant.xlf @@ -22,6 +22,21 @@ 修正方案中的所有 '{0}' + + Fix all '{0}' in containing member for '{1}' + Fix all '{0}' in containing member for '{1}' + + + + Fix all '{0}' in containing type for '{1}' + Fix all '{0}' in containing type for '{1}' + + + + Fix all '{0}' in selection for '{1}' + Fix all '{0}' in selection for '{1}' + + Project of ID {0} is required to accomplish the task but is not available from the solution 完成工作需要識別碼為 {0} 的專案,但是無法從解決方案取得 From 7812bec283046239e4968ab91473e2969ecfe706 Mon Sep 17 00:00:00 2001 From: Manish Vasani Date: Mon, 28 Feb 2022 04:46:34 -0800 Subject: [PATCH 02/18] Features layer changes for FixAll support for refactorings --- .../Core/Portable/CodeFixes/CodeFixService.cs | 2 +- .../FixAllOccurrences/FixSomeCodeAction.cs | 4 +- ...ce.cs => IFixAllCodeFixGetFixesService.cs} | 2 +- .../CodeRefactorings/CodeRefactoring.cs | 10 ++- .../CodeRefactoringService.cs | 22 ++++-- .../FixAllCodeRefactoringCodeAction.cs | 71 +++++++++++++++++++ .../IFixAllCodeRefactoringGetFixesService.cs | 27 +++++++ .../Core/Portable/FeaturesResources.resx | 9 +++ .../Portable/xlf/FeaturesResources.cs.xlf | 15 ++++ .../Portable/xlf/FeaturesResources.de.xlf | 15 ++++ .../Portable/xlf/FeaturesResources.es.xlf | 15 ++++ .../Portable/xlf/FeaturesResources.fr.xlf | 15 ++++ .../Portable/xlf/FeaturesResources.it.xlf | 15 ++++ .../Portable/xlf/FeaturesResources.ja.xlf | 15 ++++ .../Portable/xlf/FeaturesResources.ko.xlf | 15 ++++ .../Portable/xlf/FeaturesResources.pl.xlf | 15 ++++ .../Portable/xlf/FeaturesResources.pt-BR.xlf | 15 ++++ .../Portable/xlf/FeaturesResources.ru.xlf | 15 ++++ .../Portable/xlf/FeaturesResources.tr.xlf | 15 ++++ .../xlf/FeaturesResources.zh-Hans.xlf | 15 ++++ .../xlf/FeaturesResources.zh-Hant.xlf | 15 ++++ 21 files changed, 328 insertions(+), 14 deletions(-) rename src/Features/Core/Portable/CodeFixes/FixAllOccurrences/{IFixAllGetFixesService.cs => IFixAllCodeFixGetFixesService.cs} (93%) create mode 100644 src/Features/Core/Portable/CodeRefactorings/FixAllOccurences/FixAllCodeRefactoringCodeAction.cs create mode 100644 src/Features/Core/Portable/CodeRefactorings/FixAllOccurences/IFixAllCodeRefactoringGetFixesService.cs diff --git a/src/Features/Core/Portable/CodeFixes/CodeFixService.cs b/src/Features/Core/Portable/CodeFixes/CodeFixService.cs index 28d2a8ba5578a..08558a5ccc77c 100644 --- a/src/Features/Core/Portable/CodeFixes/CodeFixService.cs +++ b/src/Features/Core/Portable/CodeFixes/CodeFixService.cs @@ -280,7 +280,7 @@ public async Task ApplyCodeFixesForSpecificDiagnosticIdAsync(Document return document; } - var fixAllService = document.Project.Solution.Workspace.Services.GetRequiredService(); + var fixAllService = document.Project.Solution.Workspace.Services.GetRequiredService(); var solution = await fixAllService.GetFixAllChangedSolutionAsync( new FixAllContext(fixCollection.FixAllState, progressTracker, cancellationToken)).ConfigureAwait(false); diff --git a/src/Features/Core/Portable/CodeFixes/FixAllOccurrences/FixSomeCodeAction.cs b/src/Features/Core/Portable/CodeFixes/FixAllOccurrences/FixSomeCodeAction.cs index 66c4af66d2bd9..74ed9511fde3d 100644 --- a/src/Features/Core/Portable/CodeFixes/FixAllOccurrences/FixSomeCodeAction.cs +++ b/src/Features/Core/Portable/CodeFixes/FixAllOccurrences/FixSomeCodeAction.cs @@ -38,7 +38,7 @@ internal override Task> ComputeOperationsAsy cancellationToken.ThrowIfCancellationRequested(); FixAllLogger.LogState(FixAllState, IsInternalCodeFixProvider(FixAllState.CodeFixProvider)); - var service = FixAllState.Project.Solution.Workspace.Services.GetService(); + var service = FixAllState.Project.Solution.Workspace.Services.GetService(); var fixAllContext = new FixAllContext(FixAllState, progressTracker, cancellationToken); if (progressTracker != null) @@ -53,7 +53,7 @@ internal sealed override Task GetChangedSolutionAsync( cancellationToken.ThrowIfCancellationRequested(); FixAllLogger.LogState(FixAllState, IsInternalCodeFixProvider(FixAllState.CodeFixProvider)); - var service = FixAllState.Project.Solution.Workspace.Services.GetService(); + var service = FixAllState.Project.Solution.Workspace.Services.GetService(); var fixAllContext = new FixAllContext(FixAllState, progressTracker, cancellationToken); if (progressTracker != null) diff --git a/src/Features/Core/Portable/CodeFixes/FixAllOccurrences/IFixAllGetFixesService.cs b/src/Features/Core/Portable/CodeFixes/FixAllOccurrences/IFixAllCodeFixGetFixesService.cs similarity index 93% rename from src/Features/Core/Portable/CodeFixes/FixAllOccurrences/IFixAllGetFixesService.cs rename to src/Features/Core/Portable/CodeFixes/FixAllOccurrences/IFixAllCodeFixGetFixesService.cs index 8e5c98c878c5b..7fa9f644a4651 100644 --- a/src/Features/Core/Portable/CodeFixes/FixAllOccurrences/IFixAllGetFixesService.cs +++ b/src/Features/Core/Portable/CodeFixes/FixAllOccurrences/IFixAllCodeFixGetFixesService.cs @@ -11,7 +11,7 @@ namespace Microsoft.CodeAnalysis.CodeFixes { - internal interface IFixAllGetFixesService : IWorkspaceService + internal interface IFixAllCodeFixGetFixesService : IWorkspaceService { /// /// Computes the fix all occurrences code fix, brings up the preview changes dialog for the fix and diff --git a/src/Features/Core/Portable/CodeRefactorings/CodeRefactoring.cs b/src/Features/Core/Portable/CodeRefactorings/CodeRefactoring.cs index e675d46fcdd45..9a4fdb8779d66 100644 --- a/src/Features/Core/Portable/CodeRefactorings/CodeRefactoring.cs +++ b/src/Features/Core/Portable/CodeRefactorings/CodeRefactoring.cs @@ -2,8 +2,6 @@ // 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.Immutable; using Microsoft.CodeAnalysis.CodeActions; @@ -27,10 +25,16 @@ internal class CodeRefactoring /// public ImmutableArray<(CodeAction action, TextSpan? applicableToSpan)> CodeActions { get; } - public CodeRefactoring(CodeRefactoringProvider provider, ImmutableArray<(CodeAction, TextSpan?)> actions) + public FixAllProviderInfo? FixAllProviderInfo { get; } + + public CodeRefactoring( + CodeRefactoringProvider provider, + ImmutableArray<(CodeAction, TextSpan?)> actions, + FixAllProviderInfo? fixAllProviderInfo) { Provider = provider; CodeActions = actions.NullToEmpty(); + FixAllProviderInfo = fixAllProviderInfo; if (CodeActions.IsEmpty) { diff --git a/src/Features/Core/Portable/CodeRefactorings/CodeRefactoringService.cs b/src/Features/Core/Portable/CodeRefactorings/CodeRefactoringService.cs index cfb19127cff22..9fe43b8a6d4e6 100644 --- a/src/Features/Core/Portable/CodeRefactorings/CodeRefactoringService.cs +++ b/src/Features/Core/Portable/CodeRefactorings/CodeRefactoringService.cs @@ -33,6 +33,7 @@ internal sealed class CodeRefactoringService : ICodeRefactoringService private readonly ConditionalWeakTable _analyzerReferenceToRefactoringsMap = new(); private readonly ConditionalWeakTable.CreateValueCallback _createProjectCodeRefactoringsProvider = new(r => new ProjectCodeRefactoringProvider(r)); + private ImmutableDictionary _fixAllProviderMap = ImmutableDictionary.Empty; [ImportingConstructor] [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] @@ -95,7 +96,7 @@ public async Task HasRefactoringsAsync( RefactoringToMetadataMap.TryGetValue(provider, out var providerMetadata); var refactoring = await GetRefactoringFromProviderAsync( - document, state, provider, providerMetadata, extensionManager, options, cancellationToken).ConfigureAwait(false); + document, state, provider, providerMetadata, extensionManager, options, _fixAllProviderMap, cancellationToken).ConfigureAwait(false); if (refactoring != null) { @@ -132,7 +133,8 @@ public async Task> GetRefactoringsAsync( using (addOperationScope(providerName)) using (RoslynEventSource.LogInformationalBlock(FunctionId.Refactoring_CodeRefactoringService_GetRefactoringsAsync, providerName, cancellationToken)) { - return GetRefactoringFromProviderAsync(document, state, provider, providerMetadata, extensionManager, options, cancellationToken); + return GetRefactoringFromProviderAsync(document, state, provider, providerMetadata, + extensionManager, options, _fixAllProviderMap, cancellationToken); } }, cancellationToken)); @@ -150,6 +152,7 @@ public async Task> GetRefactoringsAsync( CodeChangeProviderMetadata? providerMetadata, IExtensionManager extensionManager, CodeActionOptions options, + ImmutableDictionary fixAllProviderMap, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); @@ -183,11 +186,16 @@ public async Task> GetRefactoringsAsync( var task = provider.ComputeRefactoringsAsync(context) ?? Task.CompletedTask; await task.ConfigureAwait(false); - var result = actions.Count > 0 - ? new CodeRefactoring(provider, actions.ToImmutable()) - : null; - - return result; + if (actions.Count > 0) + { + var fixAllProviderInfo = extensionManager.PerformFunction( + provider, () => ImmutableInterlocked.GetOrAdd(ref fixAllProviderMap, provider, FixAllProviderInfo.Create), defaultValue: null); + return new CodeRefactoring(provider, actions.ToImmutable(), fixAllProviderInfo); + } + else + { + return null; + } } catch (OperationCanceledException) { diff --git a/src/Features/Core/Portable/CodeRefactorings/FixAllOccurences/FixAllCodeRefactoringCodeAction.cs b/src/Features/Core/Portable/CodeRefactorings/FixAllOccurences/FixAllCodeRefactoringCodeAction.cs new file mode 100644 index 0000000000000..a951a7537dc06 --- /dev/null +++ b/src/Features/Core/Portable/CodeRefactorings/FixAllOccurences/FixAllCodeRefactoringCodeAction.cs @@ -0,0 +1,71 @@ +// 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.Collections.Immutable; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.CodeActions; +using Microsoft.CodeAnalysis.Shared.Utilities; + +namespace Microsoft.CodeAnalysis.CodeRefactorings +{ + internal sealed class FixAllCodeRefactoringCodeAction : CodeAction + { + internal readonly FixAllState FixAllState; + + internal FixAllCodeRefactoringCodeAction(FixAllState fixAllState) + { + FixAllState = fixAllState; + } + + public override string Title + => this.FixAllState.FixAllScope switch + { + FixAllScope.Document => FeaturesResources.Document, + FixAllScope.Project => FeaturesResources.Project, + FixAllScope.Solution => FeaturesResources.Solution, + FixAllScope.Selection => FeaturesResources.Selection, + FixAllScope.ContainingMember => FeaturesResources.Containing_Member, + FixAllScope.ContainingType => FeaturesResources.Containing_Type, + _ => throw new NotSupportedException(), + }; + + internal override string Message => FeaturesResources.Computing_fix_all_occurrences_code_fix; + + protected override async Task> ComputeOperationsAsync(CancellationToken cancellationToken) + => await ComputeOperationsAsync(new ProgressTracker(), cancellationToken).ConfigureAwait(false); + + internal override Task> ComputeOperationsAsync( + IProgressTracker progressTracker, CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + + var service = FixAllState.Project.Solution.Workspace.Services.GetService(); + + var fixAllContext = new FixAllContext(FixAllState, progressTracker, cancellationToken); + if (progressTracker != null) + progressTracker.Description = FixAllContextHelper.GetDefaultFixAllTitle(fixAllContext); + + return service.GetFixAllOperationsAsync(fixAllContext); + } + + internal sealed override Task GetChangedSolutionAsync( + IProgressTracker progressTracker, CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + + var service = FixAllState.Project.Solution.Workspace.Services.GetService(); + + var fixAllContext = new FixAllContext(FixAllState, progressTracker, cancellationToken); + if (progressTracker != null) + progressTracker.Description = FixAllContextHelper.GetDefaultFixAllTitle(fixAllContext); + + return service.GetFixAllChangedSolutionAsync(fixAllContext); + } + } +} diff --git a/src/Features/Core/Portable/CodeRefactorings/FixAllOccurences/IFixAllCodeRefactoringGetFixesService.cs b/src/Features/Core/Portable/CodeRefactorings/FixAllOccurences/IFixAllCodeRefactoringGetFixesService.cs new file mode 100644 index 0000000000000..ae955c311547d --- /dev/null +++ b/src/Features/Core/Portable/CodeRefactorings/FixAllOccurences/IFixAllCodeRefactoringGetFixesService.cs @@ -0,0 +1,27 @@ +// 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.Collections.Immutable; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.CodeActions; +using Microsoft.CodeAnalysis.Host; + +namespace Microsoft.CodeAnalysis.CodeRefactorings +{ + internal interface IFixAllCodeRefactoringGetFixesService : IWorkspaceService + { + /// + /// Computes the fix all occurrences code fix, brings up the preview changes dialog for the fix and + /// returns the code action operations corresponding to the fix. + /// + Task> GetFixAllOperationsAsync(FixAllContext fixAllContext); + + /// + /// Computes the fix all occurrences code fix and returns the changed solution. + /// + Task GetFixAllChangedSolutionAsync(FixAllContext fixAllContext); + } +} diff --git a/src/Features/Core/Portable/FeaturesResources.resx b/src/Features/Core/Portable/FeaturesResources.resx index 0b3f3598cc546..a8b0a8f9a2ce6 100644 --- a/src/Features/Core/Portable/FeaturesResources.resx +++ b/src/Features/Core/Portable/FeaturesResources.resx @@ -737,6 +737,15 @@ Do you want to continue? Solution + + Selection + + + Containing Member + + + Containing Type + TODO: dispose managed state (managed objects) diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.cs.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.cs.xlf index b17d57b131e40..ecdf9c3db58c6 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.cs.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.cs.xlf @@ -440,6 +440,16 @@ Ujistěte se, že specifikátor tt použijete pro jazyky, pro které je nezbytn Constructors not allowed + + Containing Member + Containing Member + + + + Containing Type + Containing Type + + Convert to LINQ Převést na LINQ @@ -2450,6 +2460,11 @@ Pozitivní kontrolní výrazy zpětného vyhledávání s nulovou délkou se obv Hrubá úprava + + Selection + Selection + + Selection does not contain a valid token. Výběr neobsahuje platný token. diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.de.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.de.xlf index d8eba792ed050..d4f1fb2860f46 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.de.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.de.xlf @@ -440,6 +440,16 @@ Stellen Sie sicher, dass Sie den Bezeichner "tt" für Sprachen verwenden, für d Constructors not allowed + + Containing Member + Containing Member + + + + Containing Type + Containing Type + + Convert to LINQ In LINQ konvertieren @@ -2450,6 +2460,11 @@ Positive Lookbehindassertionen mit Nullbreite werden normalerweise am Anfang reg Grobe Bearbeitung + + Selection + Selection + + Selection does not contain a valid token. Auswahl enthält kein gültiges Token. diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.es.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.es.xlf index 7833a29910cae..3dd709f9ffe44 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.es.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.es.xlf @@ -440,6 +440,16 @@ Asegúrese de usar el especificador "tt" para los idiomas para los que es necesa Constructors not allowed + + Containing Member + Containing Member + + + + Containing Type + Containing Type + + Convert to LINQ Convertir a LINQ @@ -2450,6 +2460,11 @@ Las aserciones de búsqueda retrasada (lookbehind) positivas de ancho cero se us Edición superficial + + Selection + Selection + + Selection does not contain a valid token. La selección no contiene un token válido diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.fr.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.fr.xlf index 7e28c69e6e8c5..9dfefb76694d4 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.fr.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.fr.xlf @@ -440,6 +440,16 @@ Veillez à utiliser le spécificateur "tt" pour les langues où il est nécessai Constructors not allowed + + Containing Member + Containing Member + + + + Containing Type + Containing Type + + Convert to LINQ Convertir en LINQ @@ -2450,6 +2460,11 @@ Les assertions arrière positives de largeur nulle sont généralement utilisée Modification non applicable + + Selection + Selection + + Selection does not contain a valid token. La sélection ne contient pas un jeton valide. diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.it.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.it.xlf index 1b07cde1483b7..8b058201320e9 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.it.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.it.xlf @@ -440,6 +440,16 @@ Assicurarsi di usare l'identificatore "tt" per le lingue per le quali è necessa Constructors not allowed + + Containing Member + Containing Member + + + + Containing Type + Containing Type + + Convert to LINQ Converti in LINQ @@ -2450,6 +2460,11 @@ Le asserzioni lookbehind positive di larghezza zero vengono usate in genere all' Modifica non applicabile + + Selection + Selection + + Selection does not contain a valid token. La selezione non contiene un token valido. diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.ja.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.ja.xlf index 3518e47ac51d7..ebfa036d2bc2d 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.ja.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.ja.xlf @@ -440,6 +440,16 @@ Make sure to use the "tt" specifier for languages for which it's necessary to ma Constructors not allowed + + Containing Member + Containing Member + + + + Containing Type + Containing Type + + Convert to LINQ LINQ に変換 @@ -2450,6 +2460,11 @@ Zero-width positive lookbehind assertions are typically used at the beginning of Rude 編集 + + Selection + Selection + + Selection does not contain a valid token. 選択には有効なトークンは含まれません。 diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.ko.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.ko.xlf index d5cd4943a0a70..15deb4dfa9178 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.ko.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.ko.xlf @@ -440,6 +440,16 @@ Make sure to use the "tt" specifier for languages for which it's necessary to ma Constructors not allowed + + Containing Member + Containing Member + + + + Containing Type + Containing Type + + Convert to LINQ LINQ로 변환 @@ -2450,6 +2460,11 @@ Zero-width positive lookbehind assertions are typically used at the beginning of 편집 다시 실행 + + Selection + Selection + + Selection does not contain a valid token. 선택 항목에 유효한 토큰이 포함되어 있지 않습니다. diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.pl.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.pl.xlf index 50a84b6da2dfa..636e3114604c7 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.pl.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.pl.xlf @@ -440,6 +440,16 @@ Pamiętaj, aby nie używać specyfikatora „tt” dla wszystkich języków, w k Constructors not allowed + + Containing Member + Containing Member + + + + Containing Type + Containing Type + + Convert to LINQ Konwertuj na składnię LINQ @@ -2450,6 +2460,11 @@ Pozytywne asercje wsteczne o zerowej szerokości są zwykle używane na początk Edycja reguły + + Selection + Selection + + Selection does not contain a valid token. Zaznaczenie nie zawiera prawidłowego tokenu. diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.pt-BR.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.pt-BR.xlf index fa5d7ad61c39c..d32e2235c02e5 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.pt-BR.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.pt-BR.xlf @@ -440,6 +440,16 @@ Verifique se o especificador "tt" foi usado para idiomas para os quais é necess Constructors not allowed + + Containing Member + Containing Member + + + + Containing Type + Containing Type + + Convert to LINQ Converter para LINQ @@ -2450,6 +2460,11 @@ As declarações de lookbehind positivas de largura zero normalmente são usadas Edição rudimentar + + Selection + Selection + + Selection does not contain a valid token. A seleção não contém um token válido. diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.ru.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.ru.xlf index 3476b7e7900b7..ff2f8ed7184d0 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.ru.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.ru.xlf @@ -440,6 +440,16 @@ Make sure to use the "tt" specifier for languages for which it's necessary to ma Constructors not allowed + + Containing Member + Containing Member + + + + Containing Type + Containing Type + + Convert to LINQ Преобразовать в LINQ @@ -2450,6 +2460,11 @@ Zero-width positive lookbehind assertions are typically used at the beginning of Грубая редакция + + Selection + Selection + + Selection does not contain a valid token. Выделение не содержит допустимый токен. diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.tr.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.tr.xlf index 1922e8a8861e3..f666cdb68a536 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.tr.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.tr.xlf @@ -440,6 +440,16 @@ AM ve PM arasındaki farkın korunmasının gerekli olduğu diller için "tt" be Constructors not allowed + + Containing Member + Containing Member + + + + Containing Type + Containing Type + + Convert to LINQ LINQ to dönüştürme @@ -2450,6 +2460,11 @@ Sıfır genişlikli pozitif geri yönlü onaylamalar genellikle normal ifadeleri İşlenmemiş düzenleme + + Selection + Selection + + Selection does not contain a valid token. Seçim, geçerli bir belirteç içermiyor. diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hans.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hans.xlf index 385cee9491017..f57e78bc10ecd 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hans.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hans.xlf @@ -440,6 +440,16 @@ Make sure to use the "tt" specifier for languages for which it's necessary to ma Constructors not allowed + + Containing Member + Containing Member + + + + Containing Type + Containing Type + + Convert to LINQ 转换为 LINQ @@ -2450,6 +2460,11 @@ Zero-width positive lookbehind assertions are typically used at the beginning of 原始编辑 + + Selection + Selection + + Selection does not contain a valid token. 所选内容不包含有效令牌。 diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hant.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hant.xlf index fdfb9cf7132b8..b368634ae5268 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hant.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hant.xlf @@ -440,6 +440,16 @@ Make sure to use the "tt" specifier for languages for which it's necessary to ma Constructors not allowed + + Containing Member + Containing Member + + + + Containing Type + Containing Type + + Convert to LINQ 轉換至 LINQ @@ -2450,6 +2460,11 @@ Zero-width positive lookbehind assertions are typically used at the beginning of 粗略編輯 + + Selection + Selection + + Selection does not contain a valid token. 選取範圍沒有包含有效的語彙基元。 From 26c35ef3ada7ecb244d89f2e3f8531ddaf260f25 Mon Sep 17 00:00:00 2001 From: Manish Vasani Date: Mon, 28 Feb 2022 04:53:38 -0800 Subject: [PATCH 03/18] Unified suggestion related changes for FixAll support for refactorings --- .../IFixAllCodeRefactoringSuggestedAction.cs | 20 + .../UnifiedCodeRefactoringSuggestedAction.cs | 6 +- ...iedFixAllCodeRefactoringSuggestedAction.cs | 29 ++ ...uggestedActionsSource.CodeFixesComputer.cs | 380 ++++++++++++++ ...dActionsSource.CodeRefactoringsComputer.cs | 254 +++++++++ .../UnifiedSuggestedActionsSource.cs | 487 +----------------- 6 files changed, 710 insertions(+), 466 deletions(-) create mode 100644 src/Features/Core/Portable/UnifiedSuggestions/UnifiedSuggestedActions/IFixAllCodeRefactoringSuggestedAction.cs create mode 100644 src/Features/Core/Portable/UnifiedSuggestions/UnifiedSuggestedActions/UnifiedFixAllCodeRefactoringSuggestedAction.cs create mode 100644 src/Features/Core/Portable/UnifiedSuggestions/UnifiedSuggestedActionsSource.CodeFixesComputer.cs create mode 100644 src/Features/Core/Portable/UnifiedSuggestions/UnifiedSuggestedActionsSource.CodeRefactoringsComputer.cs diff --git a/src/Features/Core/Portable/UnifiedSuggestions/UnifiedSuggestedActions/IFixAllCodeRefactoringSuggestedAction.cs b/src/Features/Core/Portable/UnifiedSuggestions/UnifiedSuggestedActions/IFixAllCodeRefactoringSuggestedAction.cs new file mode 100644 index 0000000000000..b90fbe38faa1b --- /dev/null +++ b/src/Features/Core/Portable/UnifiedSuggestions/UnifiedSuggestedActions/IFixAllCodeRefactoringSuggestedAction.cs @@ -0,0 +1,20 @@ +// 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.CodeActions; +using Microsoft.CodeAnalysis.CodeRefactorings; + +namespace Microsoft.CodeAnalysis.UnifiedSuggestions.UnifiedSuggestedActions +{ + /// + /// Common interface used by both local Roslyn and LSP to implement + /// their specific versions of FixAllCodeRefactoringSuggestedAction. + /// + internal interface IFixAllCodeRefactoringSuggestedAction + { + CodeAction OriginalCodeAction { get; } + + FixAllState? FixAllState { get; } + } +} diff --git a/src/Features/Core/Portable/UnifiedSuggestions/UnifiedSuggestedActions/UnifiedCodeRefactoringSuggestedAction.cs b/src/Features/Core/Portable/UnifiedSuggestions/UnifiedSuggestedActions/UnifiedCodeRefactoringSuggestedAction.cs index c553d3efbf2de..0510063053db6 100644 --- a/src/Features/Core/Portable/UnifiedSuggestions/UnifiedSuggestedActions/UnifiedCodeRefactoringSuggestedAction.cs +++ b/src/Features/Core/Portable/UnifiedSuggestions/UnifiedSuggestedActions/UnifiedCodeRefactoringSuggestedAction.cs @@ -16,14 +16,18 @@ internal class UnifiedCodeRefactoringSuggestedAction : UnifiedSuggestedAction, I { public CodeRefactoringProvider CodeRefactoringProvider { get; } + public UnifiedSuggestedActionSet? FixAllFlavors { get; } + public UnifiedCodeRefactoringSuggestedAction( Workspace workspace, CodeAction codeAction, CodeActionPriority codeActionPriority, - CodeRefactoringProvider codeRefactoringProvider) + CodeRefactoringProvider codeRefactoringProvider, + UnifiedSuggestedActionSet? fixAllFlavors) : base(workspace, codeAction, codeActionPriority) { CodeRefactoringProvider = codeRefactoringProvider; + FixAllFlavors = fixAllFlavors; } } } diff --git a/src/Features/Core/Portable/UnifiedSuggestions/UnifiedSuggestedActions/UnifiedFixAllCodeRefactoringSuggestedAction.cs b/src/Features/Core/Portable/UnifiedSuggestions/UnifiedSuggestedActions/UnifiedFixAllCodeRefactoringSuggestedAction.cs new file mode 100644 index 0000000000000..f05c4442d0b8b --- /dev/null +++ b/src/Features/Core/Portable/UnifiedSuggestions/UnifiedSuggestedActions/UnifiedFixAllCodeRefactoringSuggestedAction.cs @@ -0,0 +1,29 @@ +// 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.CodeActions; +using Microsoft.CodeAnalysis.CodeRefactorings; +using Microsoft.CodeAnalysis.UnifiedSuggestions.UnifiedSuggestedActions; + +namespace Microsoft.CodeAnalysis.UnifiedSuggestions +{ + /// + /// Similar to FixAllCodeRefactoringSuggestedAction, but in a location that can be used by + /// both local Roslyn and LSP. + /// + internal class UnifiedFixAllCodeRefactoringSuggestedAction : UnifiedSuggestedAction, IFixAllCodeRefactoringSuggestedAction + { + public FixAllState? FixAllState { get; } + + public UnifiedFixAllCodeRefactoringSuggestedAction( + Workspace workspace, + CodeAction codeAction, + CodeActionPriority codeActionPriority, + FixAllState? fixAllState) + : base(workspace, codeAction, codeActionPriority) + { + FixAllState = fixAllState; + } + } +} diff --git a/src/Features/Core/Portable/UnifiedSuggestions/UnifiedSuggestedActionsSource.CodeFixesComputer.cs b/src/Features/Core/Portable/UnifiedSuggestions/UnifiedSuggestedActionsSource.CodeFixesComputer.cs new file mode 100644 index 0000000000000..bd5cd402ca15a --- /dev/null +++ b/src/Features/Core/Portable/UnifiedSuggestions/UnifiedSuggestedActionsSource.CodeFixesComputer.cs @@ -0,0 +1,380 @@ +// 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 System.Collections.Generic; +using System.Collections.Immutable; +using System.Diagnostics; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.CodeActions; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.PooledObjects; +using Microsoft.CodeAnalysis.Text; +using Roslyn.Utilities; +using static Microsoft.CodeAnalysis.CodeActions.CodeAction; +using CodeFixGroupKey = System.Tuple; + +namespace Microsoft.CodeAnalysis.UnifiedSuggestions +{ + internal partial class UnifiedSuggestedActionsSource + { + /// + /// Gets, filters, and orders code fixes. + /// + private static class CodeFixesComputer + { + public static async ValueTask> GetFilterAndOrderCodeFixesAsync( + Workspace workspace, + ICodeFixService codeFixService, + Document document, + TextSpan selection, + CodeActionRequestPriority priority, + CodeActionOptions options, + Func addOperationScope, + CancellationToken cancellationToken) + { + // Intentionally switch to a threadpool thread to compute fixes. We do not want to accidentally + // run any of this on the UI thread and potentially allow any code to take a dependency on that. + var fixes = await Task.Run(() => codeFixService.GetFixesAsync( + document, + selection, + priority, + options, + addOperationScope, + cancellationToken), cancellationToken).ConfigureAwait(false); + + var filteredFixes = fixes.WhereAsArray(c => c.Fixes.Length > 0); + var organizedFixes = OrganizeFixes(workspace, filteredFixes); + + return organizedFixes; + } + + /// + /// Arrange fixes into groups based on the issue (diagnostic being fixed) and prioritize these groups. + /// + private static ImmutableArray OrganizeFixes( + Workspace workspace, + ImmutableArray fixCollections) + { + var map = ImmutableDictionary.CreateBuilder>(); + using var _ = ArrayBuilder.GetInstance(out var order); + + // First group fixes by diagnostic and priority. + GroupFixes(workspace, fixCollections, map, order); + + // Then prioritize between the groups. + var prioritizedFixes = PrioritizeFixGroups(map.ToImmutable(), order.ToImmutable(), workspace); + return prioritizedFixes; + } + + /// + /// Groups fixes by the diagnostic being addressed by each fix. + /// + private static void GroupFixes( + Workspace workspace, + ImmutableArray fixCollections, + IDictionary> map, + ArrayBuilder order) + { + foreach (var fixCollection in fixCollections) + ProcessFixCollection(workspace, map, order, fixCollection); + } + + private static void ProcessFixCollection( + Workspace workspace, + IDictionary> map, + ArrayBuilder order, + CodeFixCollection fixCollection) + { + var fixes = fixCollection.Fixes; + var fixCount = fixes.Length; + + var nonSupressionCodeFixes = fixes.WhereAsArray(f => !IsTopLevelSuppressionAction(f.Action)); + var supressionCodeFixes = fixes.WhereAsArray(f => IsTopLevelSuppressionAction(f.Action)); + + AddCodeActions(workspace, map, order, fixCollection, + GetFixAllSuggestedActionSet, nonSupressionCodeFixes); + + // Add suppression fixes to the end of a given SuggestedActionSet so that they + // always show up last in a group. + AddCodeActions(workspace, map, order, fixCollection, + GetFixAllSuggestedActionSet, supressionCodeFixes); + + return; + + // Local functions + UnifiedSuggestedActionSet? GetFixAllSuggestedActionSet(CodeAction codeAction) + => GetUnifiedFixAllSuggestedActionSet( + codeAction, fixCount, fixCollection.FixAllState, + fixCollection.SupportedScopes, fixCollection.FirstDiagnostic, + workspace); + } + + private static void AddCodeActions( + Workspace workspace, IDictionary> map, + ArrayBuilder order, CodeFixCollection fixCollection, + Func getFixAllSuggestedActionSet, + ImmutableArray codeFixes) + { + foreach (var fix in codeFixes) + { + var unifiedSuggestedAction = GetUnifiedSuggestedAction(fix.Action, fix); + AddFix(fix, unifiedSuggestedAction, map, order); + } + + return; + + // Local functions + IUnifiedSuggestedAction GetUnifiedSuggestedAction(CodeAction action, CodeFix fix) + { + if (action.NestedCodeActions.Length > 0) + { + var nestedActions = action.NestedCodeActions.SelectAsArray( + nestedAction => GetUnifiedSuggestedAction(nestedAction, fix)); + + var set = new UnifiedSuggestedActionSet( + categoryName: null, + actions: nestedActions, + title: null, + priority: GetUnifiedSuggestedActionSetPriority(action.Priority), + applicableToSpan: fix.PrimaryDiagnostic.Location.SourceSpan); + + return new UnifiedSuggestedActionWithNestedActions( + workspace, action, action.Priority, fixCollection.Provider, ImmutableArray.Create(set)); + } + else + { + return new UnifiedCodeFixSuggestedAction( + workspace, action, action.Priority, fix, fixCollection.Provider, + getFixAllSuggestedActionSet(action)); + } + } + } + + private static void AddFix( + CodeFix fix, IUnifiedSuggestedAction suggestedAction, + IDictionary> map, + ArrayBuilder order) + { + var groupKey = GetGroupKey(fix); + if (!map.ContainsKey(groupKey)) + { + order.Add(groupKey); + map[groupKey] = ImmutableArray.CreateBuilder(); + } + + map[groupKey].Add(suggestedAction); + return; + + static CodeFixGroupKey GetGroupKey(CodeFix fix) + { + var diag = fix.GetPrimaryDiagnosticData(); + if (fix.Action is AbstractConfigurationActionWithNestedActions configurationAction) + { + return new CodeFixGroupKey( + diag, configurationAction.Priority, configurationAction.AdditionalPriority); + } + + return new CodeFixGroupKey(diag, fix.Action.Priority, null); + } + } + + // If the provided fix all context is non-null and the context's code action Id matches + // the given code action's Id, returns the set of fix all occurrences actions associated + // with the code action. + private static UnifiedSuggestedActionSet? GetUnifiedFixAllSuggestedActionSet( + CodeAction action, + int actionCount, + FixAllState fixAllState, + ImmutableArray supportedScopes, + Diagnostic firstDiagnostic, + Workspace workspace) + { + + if (fixAllState == null) + { + return null; + } + + if (actionCount > 1 && action.EquivalenceKey == null) + { + return null; + } + + using var fixAllSuggestedActionsDisposer = ArrayBuilder.GetInstance(out var fixAllSuggestedActions); + foreach (var scope in supportedScopes) + { + var fixAllStateForScope = fixAllState.With(scope: scope, codeActionEquivalenceKey: action.EquivalenceKey); + var fixAllSuggestedAction = new UnifiedFixAllSuggestedAction( + workspace, action, action.Priority, fixAllStateForScope, firstDiagnostic); + + fixAllSuggestedActions.Add(fixAllSuggestedAction); + } + + return new UnifiedSuggestedActionSet( + categoryName: null, + actions: fixAllSuggestedActions.ToImmutable(), + title: CodeFixesResources.Fix_all_occurrences_in, + priority: UnifiedSuggestedActionSetPriority.Lowest, + applicableToSpan: null); + } + + /// + /// Return prioritized set of fix groups such that fix group for suppression always show up at the bottom of the list. + /// + /// + /// Fix groups are returned in priority order determined based on . + /// Priority for all s containing fixes is set to + /// by default. + /// The only exception is the case where a only contains suppression fixes - + /// the priority of such s is set to + /// so that suppression fixes + /// always show up last after all other fixes (and refactorings) for the selected line of code. + /// + private static ImmutableArray PrioritizeFixGroups( + ImmutableDictionary> map, + ImmutableArray order, + Workspace workspace) + { + using var _1 = ArrayBuilder.GetInstance(out var nonSuppressionSets); + using var _2 = ArrayBuilder.GetInstance(out var suppressionSets); + using var _3 = ArrayBuilder.GetInstance(out var bulkConfigurationActions); + + foreach (var groupKey in order) + { + var actions = map[groupKey]; + + var nonSuppressionActions = actions.Where(a => !IsTopLevelSuppressionAction(a.OriginalCodeAction)).ToImmutableArray(); + AddUnifiedSuggestedActionsSet(nonSuppressionActions, groupKey, nonSuppressionSets); + + var suppressionActions = actions.Where(a => IsTopLevelSuppressionAction(a.OriginalCodeAction) && + !IsBulkConfigurationAction(a.OriginalCodeAction)).ToImmutableArray(); + AddUnifiedSuggestedActionsSet(suppressionActions, groupKey, suppressionSets); + + bulkConfigurationActions.AddRange(actions.Where(a => IsBulkConfigurationAction(a.OriginalCodeAction))); + } + + var sets = nonSuppressionSets.ToImmutable(); + + // Append bulk configuration fixes at the end of suppression/configuration fixes. + if (bulkConfigurationActions.Count > 0) + { + var bulkConfigurationSet = new UnifiedSuggestedActionSet( + UnifiedPredefinedSuggestedActionCategoryNames.CodeFix, + bulkConfigurationActions.ToImmutable(), + title: null, + priority: UnifiedSuggestedActionSetPriority.Lowest, + applicableToSpan: null); + suppressionSets.Add(bulkConfigurationSet); + } + + if (suppressionSets.Count > 0) + { + // Wrap the suppression/configuration actions within another top level suggested action + // to avoid clutter in the light bulb menu. + var suppressOrConfigureCodeAction = new NoChangeAction(CodeFixesResources.Suppress_or_Configure_issues, nameof(CodeFixesResources.Suppress_or_Configure_issues)); + var wrappingSuggestedAction = new UnifiedSuggestedActionWithNestedActions( + workspace, codeAction: suppressOrConfigureCodeAction, + codeActionPriority: suppressOrConfigureCodeAction.Priority, provider: null, + nestedActionSets: suppressionSets.ToImmutable()); + + // Combine the spans and the category of each of the nested suggested actions + // to get the span and category for the new top level suggested action. + var (span, category) = CombineSpansAndCategory(suppressionSets); + var wrappingSet = new UnifiedSuggestedActionSet( + category, + actions: ImmutableArray.Create(wrappingSuggestedAction), + title: CodeFixesResources.Suppress_or_Configure_issues, + priority: UnifiedSuggestedActionSetPriority.Lowest, + applicableToSpan: span); + sets = sets.Add(wrappingSet); + } + + return sets; + + // Local functions + static (TextSpan? span, string category) CombineSpansAndCategory(ArrayBuilder sets) + { + // We are combining the spans and categories of the given set of suggested action sets + // to generate a result span containing the spans of individual suggested action sets and + // a result category which is the maximum severity category amongst the set + var minStart = -1; + var maxEnd = -1; + var category = UnifiedPredefinedSuggestedActionCategoryNames.CodeFix; + + foreach (var set in sets) + { + if (set.ApplicableToSpan.HasValue) + { + var currentStart = set.ApplicableToSpan.Value.Start; + var currentEnd = set.ApplicableToSpan.Value.End; + + if (minStart == -1 || currentStart < minStart) + { + minStart = currentStart; + } + + if (maxEnd == -1 || currentEnd > maxEnd) + { + maxEnd = currentEnd; + } + } + + Debug.Assert(set.CategoryName is UnifiedPredefinedSuggestedActionCategoryNames.CodeFix or + UnifiedPredefinedSuggestedActionCategoryNames.ErrorFix); + + // If this set contains an error fix, then change the result category to ErrorFix + if (set.CategoryName == UnifiedPredefinedSuggestedActionCategoryNames.ErrorFix) + { + category = UnifiedPredefinedSuggestedActionCategoryNames.ErrorFix; + } + } + + var combinedSpan = minStart >= 0 ? TextSpan.FromBounds(minStart, maxEnd) : (TextSpan?)null; + return (combinedSpan, category); + } + } + + private static void AddUnifiedSuggestedActionsSet( + ImmutableArray actions, + CodeFixGroupKey groupKey, + ArrayBuilder sets) + { + foreach (var group in actions.GroupBy(a => a.CodeActionPriority)) + { + var priority = GetUnifiedSuggestedActionSetPriority(group.Key); + + // diagnostic from things like build shouldn't reach here since we don't support LB for those diagnostics + Debug.Assert(groupKey.Item1.HasTextSpan); + var category = GetFixCategory(groupKey.Item1.Severity); + sets.Add(new UnifiedSuggestedActionSet( + category, group.ToImmutableArray(), title: null, priority, applicableToSpan: groupKey.Item1.GetTextSpan())); + } + } + + private static string GetFixCategory(DiagnosticSeverity severity) + { + switch (severity) + { + case DiagnosticSeverity.Hidden: + case DiagnosticSeverity.Info: + case DiagnosticSeverity.Warning: + return UnifiedPredefinedSuggestedActionCategoryNames.CodeFix; + case DiagnosticSeverity.Error: + return UnifiedPredefinedSuggestedActionCategoryNames.ErrorFix; + default: + throw ExceptionUtilities.Unreachable; + } + } + + private static bool IsTopLevelSuppressionAction(CodeAction action) + => action is AbstractConfigurationActionWithNestedActions; + + private static bool IsBulkConfigurationAction(CodeAction action) + => (action as AbstractConfigurationActionWithNestedActions)?.IsBulkConfigurationAction == true; + } + } +} diff --git a/src/Features/Core/Portable/UnifiedSuggestions/UnifiedSuggestedActionsSource.CodeRefactoringsComputer.cs b/src/Features/Core/Portable/UnifiedSuggestions/UnifiedSuggestedActionsSource.CodeRefactoringsComputer.cs new file mode 100644 index 0000000000000..b760bf29d20f0 --- /dev/null +++ b/src/Features/Core/Portable/UnifiedSuggestions/UnifiedSuggestedActionsSource.CodeRefactoringsComputer.cs @@ -0,0 +1,254 @@ +// 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 System.Collections.Generic; +using System.Collections.Immutable; +using System.Diagnostics; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.CodeActions; +using Microsoft.CodeAnalysis.CodeRefactorings; +using Microsoft.CodeAnalysis.LanguageServices; +using Microsoft.CodeAnalysis.PooledObjects; +using Microsoft.CodeAnalysis.Shared.Extensions; +using Microsoft.CodeAnalysis.Text; +using Roslyn.Utilities; +using static Microsoft.CodeAnalysis.CodeActions.CodeAction; + +namespace Microsoft.CodeAnalysis.UnifiedSuggestions +{ + /// + /// Provides mutual code action logic for both local and LSP scenarios + /// via intermediate interface . + /// + internal partial class UnifiedSuggestedActionsSource + { + private static class CodeRefactoringsComputer + { + /// + /// Gets, filters, and orders code refactorings. + /// + public static async Task> GetFilterAndOrderCodeRefactoringsAsync( + Workspace workspace, + ICodeRefactoringService codeRefactoringService, + Document document, + TextSpan selection, + CodeActionRequestPriority priority, + CodeActionOptions options, + Func addOperationScope, + bool filterOutsideSelection, + CancellationToken cancellationToken) + { + // It may seem strange that we kick off a task, but then immediately 'Wait' on + // it. However, it's deliberate. We want to make sure that the code runs on + // the background so that no one takes an accidentally dependency on running on + // the UI thread. + var refactorings = await Task.Run( + () => codeRefactoringService.GetRefactoringsAsync( + document, selection, priority, options, addOperationScope, + cancellationToken), cancellationToken).ConfigureAwait(false); + + var filteredRefactorings = FilterOnAnyThread(refactorings, selection, filterOutsideSelection); + + _ = ArrayBuilder.GetInstance(filteredRefactorings.Length, out var orderedRefactorings); + foreach (var refactoring in filteredRefactorings) + { + var orderedRefactoring = await OrganizeRefactoringsAsync(workspace, document, selection, refactoring, cancellationToken).ConfigureAwait(false); + orderedRefactorings.Add(orderedRefactoring); + } + + return orderedRefactorings.ToImmutable(); + } + + private static ImmutableArray FilterOnAnyThread( + ImmutableArray refactorings, + TextSpan selection, + bool filterOutsideSelection) + => refactorings.Select(r => FilterOnAnyThread(r, selection, filterOutsideSelection)).WhereNotNull().ToImmutableArray(); + + private static CodeRefactoring? FilterOnAnyThread( + CodeRefactoring refactoring, + TextSpan selection, + bool filterOutsideSelection) + { + var actions = refactoring.CodeActions.WhereAsArray(IsActionAndSpanApplicable); + return actions.Length == 0 + ? null + : actions.Length == refactoring.CodeActions.Length + ? refactoring + : new CodeRefactoring(refactoring.Provider, actions, refactoring.FixAllProviderInfo); + + bool IsActionAndSpanApplicable((CodeAction action, TextSpan? applicableSpan) actionAndSpan) + { + if (filterOutsideSelection) + { + // Filter out refactorings with applicable span outside the selection span. + if (!actionAndSpan.applicableSpan.HasValue || + !selection.IntersectsWith(actionAndSpan.applicableSpan.Value)) + { + return false; + } + } + + return true; + } + } + + /// + /// Arrange refactorings into groups. + /// + /// + /// Refactorings are returned in priority order determined based on . + /// Priority for all s containing refactorings is set to + /// and should show up after fixes but before + /// suppression fixes in the light bulb menu. + /// + private static async Task OrganizeRefactoringsAsync( + Workspace workspace, + Document document, + TextSpan selection, + CodeRefactoring refactoring, + CancellationToken cancellationToken) + { + using var refactoringSuggestedActionsDisposer = ArrayBuilder.GetInstance(out var refactoringSuggestedActions); + + foreach (var (action, applicableToSpan) in refactoring.CodeActions) + { + var unifiedActionSet = await GetUnifiedSuggestedActionSetAsync(action, applicableToSpan, selection, cancellationToken).ConfigureAwait(false); + refactoringSuggestedActions.Add(unifiedActionSet); + } + + var actions = refactoringSuggestedActions.ToImmutable(); + + // An action set: + // - gets the the same priority as the highest priority action within in. + // - gets `applicableToSpan` of the first action: + // - E.g. the `applicableToSpan` closest to current selection might be a more correct + // choice. All actions created by one Refactoring have usually the same `applicableSpan` + // and therefore the complexity of determining the closest one isn't worth the benefit + // of slightly more correct orderings in certain edge cases. + return new UnifiedSuggestedActionSet( + UnifiedPredefinedSuggestedActionCategoryNames.Refactoring, + actions: actions, + title: null, + priority: GetUnifiedSuggestedActionSetPriority(actions.Max(a => a.CodeActionPriority)), + applicableToSpan: refactoring.CodeActions.FirstOrDefault().applicableToSpan); + + // Local functions + async Task GetUnifiedSuggestedActionSetAsync(CodeAction codeAction, TextSpan? applicableToSpan, TextSpan selection, CancellationToken cancellationToken) + { + if (codeAction.NestedCodeActions.Length > 0) + { + _ = ArrayBuilder.GetInstance(codeAction.NestedCodeActions.Length, out var nestedActions); + foreach (var nestedAction in codeAction.NestedCodeActions) + { + var unifiedAction = await GetUnifiedSuggestedActionSetAsync(nestedAction, applicableToSpan, selection, cancellationToken).ConfigureAwait(false); + nestedActions.Add(unifiedAction); + } + + var set = new UnifiedSuggestedActionSet( + categoryName: null, + actions: nestedActions.ToImmutable(), + title: null, + priority: GetUnifiedSuggestedActionSetPriority(codeAction.Priority), + applicableToSpan: applicableToSpan); + + return new UnifiedSuggestedActionWithNestedActions( + workspace, codeAction, codeAction.Priority, refactoring.Provider, ImmutableArray.Create(set)); + } + else + { + var fixAllSuggestedActionSet = await GetUnifiedFixAllSuggestedActionSetAsync(codeAction, + refactoring.CodeActions.Length, document, selection, refactoring.Provider, + refactoring.FixAllProviderInfo, workspace, cancellationToken).ConfigureAwait(false); + + return new UnifiedCodeRefactoringSuggestedAction( + workspace, codeAction, codeAction.Priority, refactoring.Provider, fixAllSuggestedActionSet); + } + } + } + + // If the provided fix all context is non-null and the context's code action Id matches + // the given code action's Id, returns the set of fix all occurrences actions associated + // with the code action. + private static async Task GetUnifiedFixAllSuggestedActionSetAsync( + CodeAction action, + int actionCount, + Document document, + TextSpan selection, + CodeRefactoringProvider provider, + FixAllProviderInfo? fixAllProviderInfo, + Workspace workspace, + CancellationToken cancellationToken) + { + if (fixAllProviderInfo == null) + { + return null; + } + + if (actionCount > 1 && action.EquivalenceKey == null) + { + return null; + } + + using var fixAllSuggestedActionsDisposer = ArrayBuilder.GetInstance(out var fixAllSuggestedActions); + foreach (var scope in fixAllProviderInfo.SupportedScopes) + { + if (scope == FixAllScope.Selection && selection.IsEmpty) + continue; + + var fixAllSpan = await GetFixAllSpanForScopeAsync(scope).ConfigureAwait(false); + var fixAllState = new FixAllState(fixAllProviderInfo.FixAllProvider, document, provider, scope, fixAllSpan, action); + var fixAllSuggestedAction = new UnifiedFixAllCodeRefactoringSuggestedAction( + workspace, action, action.Priority, fixAllState); + + fixAllSuggestedActions.Add(fixAllSuggestedAction); + } + + return new UnifiedSuggestedActionSet( + categoryName: null, + actions: fixAllSuggestedActions.ToImmutable(), + title: CodeFixesResources.Fix_all_occurrences_in, + priority: UnifiedSuggestedActionSetPriority.Lowest, + applicableToSpan: null); + + // Local functions + async Task GetFixAllSpanForScopeAsync(FixAllScope fixAllScope) + { + return fixAllScope switch + { + FixAllScope.Selection => selection, + FixAllScope.ContainingMember or FixAllScope.ContainingType + => await GetSpanForContainingMemberOrTypeAsync(fixAllScope).ConfigureAwait(false), + _ => null, + }; + } + + async Task GetSpanForContainingMemberOrTypeAsync(FixAllScope fixAllScope) + { + Contract.ThrowIfFalse(fixAllScope is FixAllScope.ContainingMember or FixAllScope.ContainingType); + + var root = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); + var syntaxFacts = document.GetRequiredLanguageService(); + + var startContainer = fixAllScope == FixAllScope.ContainingMember + ? syntaxFacts.GetContainingMemberDeclaration(root, selection.Start) + : syntaxFacts.GetContainingTypeDeclaration(root, selection.Start); + if (selection.IsEmpty || startContainer == null) + return startContainer?.FullSpan; + + var endContainer = fixAllScope == FixAllScope.ContainingMember + ? syntaxFacts.GetContainingMemberDeclaration(root, selection.End) + : syntaxFacts.GetContainingTypeDeclaration(root, selection.End); + if (startContainer == endContainer) + return startContainer.FullSpan; + + return null; + } + } + } + } +} diff --git a/src/Features/Core/Portable/UnifiedSuggestions/UnifiedSuggestedActionsSource.cs b/src/Features/Core/Portable/UnifiedSuggestions/UnifiedSuggestedActionsSource.cs index e51c1efe4dbd3..9880dab8058e4 100644 --- a/src/Features/Core/Portable/UnifiedSuggestions/UnifiedSuggestedActionsSource.cs +++ b/src/Features/Core/Portable/UnifiedSuggestions/UnifiedSuggestedActionsSource.cs @@ -24,479 +24,36 @@ namespace Microsoft.CodeAnalysis.UnifiedSuggestions /// Provides mutual code action logic for both local and LSP scenarios /// via intermediate interface . /// - internal class UnifiedSuggestedActionsSource + internal partial class UnifiedSuggestedActionsSource { /// /// Gets, filters, and orders code fixes. /// - public static async ValueTask> GetFilterAndOrderCodeFixesAsync( - Workspace workspace, - ICodeFixService codeFixService, - Document document, - TextSpan selection, - CodeActionRequestPriority priority, - CodeActionOptions options, - Func addOperationScope, - CancellationToken cancellationToken) - { - // Intentionally switch to a threadpool thread to compute fixes. We do not want to accidentally - // run any of this on the UI thread and potentially allow any code to take a dependency on that. - var fixes = await Task.Run(() => codeFixService.GetFixesAsync( - document, - selection, - priority, - options, - addOperationScope, - cancellationToken), cancellationToken).ConfigureAwait(false); - - var filteredFixes = fixes.WhereAsArray(c => c.Fixes.Length > 0); - var organizedFixes = OrganizeFixes(workspace, filteredFixes); - - return organizedFixes; - } - - /// - /// Arrange fixes into groups based on the issue (diagnostic being fixed) and prioritize these groups. - /// - private static ImmutableArray OrganizeFixes( - Workspace workspace, - ImmutableArray fixCollections) - { - var map = ImmutableDictionary.CreateBuilder>(); - using var _ = ArrayBuilder.GetInstance(out var order); - - // First group fixes by diagnostic and priority. - GroupFixes(workspace, fixCollections, map, order); - - // Then prioritize between the groups. - var prioritizedFixes = PrioritizeFixGroups(map.ToImmutable(), order.ToImmutable(), workspace); - return prioritizedFixes; - } - - /// - /// Groups fixes by the diagnostic being addressed by each fix. - /// - private static void GroupFixes( - Workspace workspace, - ImmutableArray fixCollections, - IDictionary> map, - ArrayBuilder order) - { - foreach (var fixCollection in fixCollections) - ProcessFixCollection(workspace, map, order, fixCollection); - } - - private static void ProcessFixCollection( - Workspace workspace, - IDictionary> map, - ArrayBuilder order, - CodeFixCollection fixCollection) - { - var fixes = fixCollection.Fixes; - var fixCount = fixes.Length; - - var nonSupressionCodeFixes = fixes.WhereAsArray(f => !IsTopLevelSuppressionAction(f.Action)); - var supressionCodeFixes = fixes.WhereAsArray(f => IsTopLevelSuppressionAction(f.Action)); - - AddCodeActions(workspace, map, order, fixCollection, - GetFixAllSuggestedActionSet, nonSupressionCodeFixes); - - // Add suppression fixes to the end of a given SuggestedActionSet so that they - // always show up last in a group. - AddCodeActions(workspace, map, order, fixCollection, - GetFixAllSuggestedActionSet, supressionCodeFixes); - - return; - - // Local functions - UnifiedSuggestedActionSet? GetFixAllSuggestedActionSet(CodeAction codeAction) - => GetUnifiedFixAllSuggestedActionSet( - codeAction, fixCount, fixCollection.FixAllState, - fixCollection.SupportedScopes, fixCollection.FirstDiagnostic, - workspace); - } - - private static void AddCodeActions( - Workspace workspace, IDictionary> map, - ArrayBuilder order, CodeFixCollection fixCollection, - Func getFixAllSuggestedActionSet, - ImmutableArray codeFixes) - { - foreach (var fix in codeFixes) - { - var unifiedSuggestedAction = GetUnifiedSuggestedAction(fix.Action, fix); - AddFix(fix, unifiedSuggestedAction, map, order); - } - - return; - - // Local functions - IUnifiedSuggestedAction GetUnifiedSuggestedAction(CodeAction action, CodeFix fix) - { - if (action.NestedCodeActions.Length > 0) - { - var nestedActions = action.NestedCodeActions.SelectAsArray( - nestedAction => GetUnifiedSuggestedAction(nestedAction, fix)); - - var set = new UnifiedSuggestedActionSet( - categoryName: null, - actions: nestedActions, - title: null, - priority: GetUnifiedSuggestedActionSetPriority(action.Priority), - applicableToSpan: fix.PrimaryDiagnostic.Location.SourceSpan); - - return new UnifiedSuggestedActionWithNestedActions( - workspace, action, action.Priority, fixCollection.Provider, ImmutableArray.Create(set)); - } - else - { - return new UnifiedCodeFixSuggestedAction( - workspace, action, action.Priority, fix, fixCollection.Provider, - getFixAllSuggestedActionSet(action)); - } - } - } - - private static void AddFix( - CodeFix fix, IUnifiedSuggestedAction suggestedAction, - IDictionary> map, - ArrayBuilder order) - { - var groupKey = GetGroupKey(fix); - if (!map.ContainsKey(groupKey)) - { - order.Add(groupKey); - map[groupKey] = ImmutableArray.CreateBuilder(); - } - - map[groupKey].Add(suggestedAction); - return; - - static CodeFixGroupKey GetGroupKey(CodeFix fix) - { - var diag = fix.GetPrimaryDiagnosticData(); - if (fix.Action is AbstractConfigurationActionWithNestedActions configurationAction) - { - return new CodeFixGroupKey( - diag, configurationAction.Priority, configurationAction.AdditionalPriority); - } - - return new CodeFixGroupKey(diag, fix.Action.Priority, null); - } - } - - // If the provided fix all context is non-null and the context's code action Id matches - // the given code action's Id, returns the set of fix all occurrences actions associated - // with the code action. - private static UnifiedSuggestedActionSet? GetUnifiedFixAllSuggestedActionSet( - CodeAction action, - int actionCount, - FixAllState fixAllState, - ImmutableArray supportedScopes, - Diagnostic firstDiagnostic, - Workspace workspace) - { - - if (fixAllState == null) - { - return null; - } - - if (actionCount > 1 && action.EquivalenceKey == null) - { - return null; - } - - using var fixAllSuggestedActionsDisposer = ArrayBuilder.GetInstance(out var fixAllSuggestedActions); - foreach (var scope in supportedScopes) - { - var fixAllStateForScope = fixAllState.With(scope: scope, codeActionEquivalenceKey: action.EquivalenceKey); - var fixAllSuggestedAction = new UnifiedFixAllSuggestedAction( - workspace, action, action.Priority, fixAllStateForScope, firstDiagnostic); - - fixAllSuggestedActions.Add(fixAllSuggestedAction); - } - - return new UnifiedSuggestedActionSet( - categoryName: null, - actions: fixAllSuggestedActions.ToImmutable(), - title: CodeFixesResources.Fix_all_occurrences_in, - priority: UnifiedSuggestedActionSetPriority.Lowest, - applicableToSpan: null); - } - - /// - /// Return prioritized set of fix groups such that fix group for suppression always show up at the bottom of the list. - /// - /// - /// Fix groups are returned in priority order determined based on . - /// Priority for all s containing fixes is set to - /// by default. - /// The only exception is the case where a only contains suppression fixes - - /// the priority of such s is set to - /// so that suppression fixes - /// always show up last after all other fixes (and refactorings) for the selected line of code. - /// - private static ImmutableArray PrioritizeFixGroups( - ImmutableDictionary> map, - ImmutableArray order, - Workspace workspace) - { - using var _1 = ArrayBuilder.GetInstance(out var nonSuppressionSets); - using var _2 = ArrayBuilder.GetInstance(out var suppressionSets); - using var _3 = ArrayBuilder.GetInstance(out var bulkConfigurationActions); - - foreach (var groupKey in order) - { - var actions = map[groupKey]; - - var nonSuppressionActions = actions.Where(a => !IsTopLevelSuppressionAction(a.OriginalCodeAction)).ToImmutableArray(); - AddUnifiedSuggestedActionsSet(nonSuppressionActions, groupKey, nonSuppressionSets); - - var suppressionActions = actions.Where(a => IsTopLevelSuppressionAction(a.OriginalCodeAction) && - !IsBulkConfigurationAction(a.OriginalCodeAction)).ToImmutableArray(); - AddUnifiedSuggestedActionsSet(suppressionActions, groupKey, suppressionSets); - - bulkConfigurationActions.AddRange(actions.Where(a => IsBulkConfigurationAction(a.OriginalCodeAction))); - } - - var sets = nonSuppressionSets.ToImmutable(); - - // Append bulk configuration fixes at the end of suppression/configuration fixes. - if (bulkConfigurationActions.Count > 0) - { - var bulkConfigurationSet = new UnifiedSuggestedActionSet( - UnifiedPredefinedSuggestedActionCategoryNames.CodeFix, - bulkConfigurationActions.ToImmutable(), - title: null, - priority: UnifiedSuggestedActionSetPriority.Lowest, - applicableToSpan: null); - suppressionSets.Add(bulkConfigurationSet); - } - - if (suppressionSets.Count > 0) - { - // Wrap the suppression/configuration actions within another top level suggested action - // to avoid clutter in the light bulb menu. - var suppressOrConfigureCodeAction = new NoChangeAction(CodeFixesResources.Suppress_or_Configure_issues, nameof(CodeFixesResources.Suppress_or_Configure_issues)); - var wrappingSuggestedAction = new UnifiedSuggestedActionWithNestedActions( - workspace, codeAction: suppressOrConfigureCodeAction, - codeActionPriority: suppressOrConfigureCodeAction.Priority, provider: null, - nestedActionSets: suppressionSets.ToImmutable()); - - // Combine the spans and the category of each of the nested suggested actions - // to get the span and category for the new top level suggested action. - var (span, category) = CombineSpansAndCategory(suppressionSets); - var wrappingSet = new UnifiedSuggestedActionSet( - category, - actions: ImmutableArray.Create(wrappingSuggestedAction), - title: CodeFixesResources.Suppress_or_Configure_issues, - priority: UnifiedSuggestedActionSetPriority.Lowest, - applicableToSpan: span); - sets = sets.Add(wrappingSet); - } - - return sets; - - // Local functions - static (TextSpan? span, string category) CombineSpansAndCategory(ArrayBuilder sets) - { - // We are combining the spans and categories of the given set of suggested action sets - // to generate a result span containing the spans of individual suggested action sets and - // a result category which is the maximum severity category amongst the set - var minStart = -1; - var maxEnd = -1; - var category = UnifiedPredefinedSuggestedActionCategoryNames.CodeFix; - - foreach (var set in sets) - { - if (set.ApplicableToSpan.HasValue) - { - var currentStart = set.ApplicableToSpan.Value.Start; - var currentEnd = set.ApplicableToSpan.Value.End; - - if (minStart == -1 || currentStart < minStart) - { - minStart = currentStart; - } - - if (maxEnd == -1 || currentEnd > maxEnd) - { - maxEnd = currentEnd; - } - } - - Debug.Assert(set.CategoryName is UnifiedPredefinedSuggestedActionCategoryNames.CodeFix or - UnifiedPredefinedSuggestedActionCategoryNames.ErrorFix); - - // If this set contains an error fix, then change the result category to ErrorFix - if (set.CategoryName == UnifiedPredefinedSuggestedActionCategoryNames.ErrorFix) - { - category = UnifiedPredefinedSuggestedActionCategoryNames.ErrorFix; - } - } - - var combinedSpan = minStart >= 0 ? TextSpan.FromBounds(minStart, maxEnd) : (TextSpan?)null; - return (combinedSpan, category); - } - } - - private static void AddUnifiedSuggestedActionsSet( - ImmutableArray actions, - CodeFixGroupKey groupKey, - ArrayBuilder sets) - { - foreach (var group in actions.GroupBy(a => a.CodeActionPriority)) - { - var priority = GetUnifiedSuggestedActionSetPriority(group.Key); - - // diagnostic from things like build shouldn't reach here since we don't support LB for those diagnostics - Debug.Assert(groupKey.Item1.HasTextSpan); - var category = GetFixCategory(groupKey.Item1.Severity); - sets.Add(new UnifiedSuggestedActionSet( - category, group.ToImmutableArray(), title: null, priority, applicableToSpan: groupKey.Item1.GetTextSpan())); - } - } - - private static string GetFixCategory(DiagnosticSeverity severity) - { - switch (severity) - { - case DiagnosticSeverity.Hidden: - case DiagnosticSeverity.Info: - case DiagnosticSeverity.Warning: - return UnifiedPredefinedSuggestedActionCategoryNames.CodeFix; - case DiagnosticSeverity.Error: - return UnifiedPredefinedSuggestedActionCategoryNames.ErrorFix; - default: - throw ExceptionUtilities.Unreachable; - } - } - - private static bool IsTopLevelSuppressionAction(CodeAction action) - => action is AbstractConfigurationActionWithNestedActions; - - private static bool IsBulkConfigurationAction(CodeAction action) - => (action as AbstractConfigurationActionWithNestedActions)?.IsBulkConfigurationAction == true; + public static ValueTask> GetFilterAndOrderCodeFixesAsync( + Workspace workspace, + ICodeFixService codeFixService, + Document document, + TextSpan selection, + CodeActionRequestPriority priority, + CodeActionOptions options, + Func addOperationScope, + CancellationToken cancellationToken) + => CodeFixesComputer.GetFilterAndOrderCodeFixesAsync(workspace, codeFixService, document, selection, priority, options, addOperationScope, cancellationToken); /// /// Gets, filters, and orders code refactorings. /// - public static async Task> GetFilterAndOrderCodeRefactoringsAsync( - Workspace workspace, - ICodeRefactoringService codeRefactoringService, - Document document, - TextSpan selection, - CodeActionRequestPriority priority, - CodeActionOptions options, - Func addOperationScope, - bool filterOutsideSelection, - CancellationToken cancellationToken) - { - // It may seem strange that we kick off a task, but then immediately 'Wait' on - // it. However, it's deliberate. We want to make sure that the code runs on - // the background so that no one takes an accidentally dependency on running on - // the UI thread. - var refactorings = await Task.Run( - () => codeRefactoringService.GetRefactoringsAsync( - document, selection, priority, options, addOperationScope, - cancellationToken), cancellationToken).ConfigureAwait(false); - - var filteredRefactorings = FilterOnAnyThread(refactorings, selection, filterOutsideSelection); - - return filteredRefactorings.SelectAsArray( - r => OrganizeRefactorings(workspace, r)); - } - - private static ImmutableArray FilterOnAnyThread( - ImmutableArray refactorings, - TextSpan selection, - bool filterOutsideSelection) - => refactorings.Select(r => FilterOnAnyThread(r, selection, filterOutsideSelection)).WhereNotNull().ToImmutableArray(); - - private static CodeRefactoring? FilterOnAnyThread( - CodeRefactoring refactoring, - TextSpan selection, - bool filterOutsideSelection) - { - var actions = refactoring.CodeActions.WhereAsArray(IsActionAndSpanApplicable); - return actions.Length == 0 - ? null - : actions.Length == refactoring.CodeActions.Length - ? refactoring - : new CodeRefactoring(refactoring.Provider, actions); - - bool IsActionAndSpanApplicable((CodeAction action, TextSpan? applicableSpan) actionAndSpan) - { - if (filterOutsideSelection) - { - // Filter out refactorings with applicable span outside the selection span. - if (!actionAndSpan.applicableSpan.HasValue || - !selection.IntersectsWith(actionAndSpan.applicableSpan.Value)) - { - return false; - } - } - - return true; - } - } - - /// - /// Arrange refactorings into groups. - /// - /// - /// Refactorings are returned in priority order determined based on . - /// Priority for all s containing refactorings is set to - /// and should show up after fixes but before - /// suppression fixes in the light bulb menu. - /// - private static UnifiedSuggestedActionSet OrganizeRefactorings(Workspace workspace, CodeRefactoring refactoring) - { - using var refactoringSuggestedActionsDisposer = ArrayBuilder.GetInstance(out var refactoringSuggestedActions); - - foreach (var codeAction in refactoring.CodeActions) - { - if (codeAction.action.NestedCodeActions.Length > 0) - { - var nestedActions = codeAction.action.NestedCodeActions.SelectAsArray( - na => (IUnifiedSuggestedAction)new UnifiedCodeRefactoringSuggestedAction(workspace, na, na.Priority, refactoring.Provider)); - - var set = new UnifiedSuggestedActionSet( - categoryName: null, - actions: nestedActions, - title: null, - priority: GetUnifiedSuggestedActionSetPriority(codeAction.action.Priority), - applicableToSpan: codeAction.applicableToSpan); - - refactoringSuggestedActions.Add( - new UnifiedSuggestedActionWithNestedActions( - workspace, codeAction.action, codeAction.action.Priority, refactoring.Provider, ImmutableArray.Create(set))); - } - else - { - refactoringSuggestedActions.Add( - new UnifiedCodeRefactoringSuggestedAction( - workspace, codeAction.action, codeAction.action.Priority, refactoring.Provider)); - } - } - - var actions = refactoringSuggestedActions.ToImmutable(); - - // An action set: - // - gets the the same priority as the highest priority action within in. - // - gets `applicableToSpan` of the first action: - // - E.g. the `applicableToSpan` closest to current selection might be a more correct - // choice. All actions created by one Refactoring have usually the same `applicableSpan` - // and therefore the complexity of determining the closest one isn't worth the benefit - // of slightly more correct orderings in certain edge cases. - return new UnifiedSuggestedActionSet( - UnifiedPredefinedSuggestedActionCategoryNames.Refactoring, - actions: actions, - title: null, - priority: GetUnifiedSuggestedActionSetPriority(actions.Max(a => a.CodeActionPriority)), - applicableToSpan: refactoring.CodeActions.FirstOrDefault().applicableToSpan); - } + public static Task> GetFilterAndOrderCodeRefactoringsAsync( + Workspace workspace, + ICodeRefactoringService codeRefactoringService, + Document document, + TextSpan selection, + CodeActionRequestPriority priority, + CodeActionOptions options, + Func addOperationScope, + bool filterOutsideSelection, + CancellationToken cancellationToken) + => CodeRefactoringsComputer.GetFilterAndOrderCodeRefactoringsAsync(workspace, codeRefactoringService, document, selection, priority, options, addOperationScope, filterOutsideSelection, cancellationToken); private static UnifiedSuggestedActionSetPriority GetUnifiedSuggestedActionSetPriority(CodeActionPriority key) => key switch From c6cc64fd0ff316a5d43f84506344972724857dc0 Mon Sep 17 00:00:00 2001 From: Manish Vasani Date: Mon, 28 Feb 2022 06:49:12 -0800 Subject: [PATCH 04/18] EditorFeatures layer changes for FixAll support for refactorings --- ...ice.cs => FixAllCodeFixGetFixesService.cs} | 6 +- .../FixAllCodeRefactoringGetFixesService.cs | 220 ++++++++++++++++++ .../SuggestedActionWithNestedFlavors.cs | 4 +- .../CodeRefactoringSuggestedAction.cs | 6 +- ...odeFixSuggestedAction.FixAllCodeAction.cs} | 2 +- ...ion.cs => FixAllCodeFixSuggestedAction.cs} | 4 +- .../FixAllCodeRefactoringSuggestedAction.cs | 71 ++++++ .../Suggestions/SuggestedActionsSource.cs | 8 +- .../EditorLayerExtensionManager.cs | 2 +- .../Shared/Extensions/TelemetryExtensions.cs | 12 + 10 files changed, 322 insertions(+), 13 deletions(-) rename src/EditorFeatures/Core.Wpf/Suggestions/FixAll/{FixAllGetFixesService.cs => FixAllCodeFixGetFixesService.cs} (97%) create mode 100644 src/EditorFeatures/Core.Wpf/Suggestions/FixAll/FixAllCodeRefactoringGetFixesService.cs rename src/EditorFeatures/Core.Wpf/Suggestions/SuggestedActions/{FixAllSuggestedAction.FixAllCodeAction.cs => FixAllCodeFixSuggestedAction.FixAllCodeAction.cs} (95%) rename src/EditorFeatures/Core.Wpf/Suggestions/SuggestedActions/{FixAllSuggestedAction.cs => FixAllCodeFixSuggestedAction.cs} (94%) create mode 100644 src/EditorFeatures/Core.Wpf/Suggestions/SuggestedActions/FixAllCodeRefactoringSuggestedAction.cs diff --git a/src/EditorFeatures/Core.Wpf/Suggestions/FixAll/FixAllGetFixesService.cs b/src/EditorFeatures/Core.Wpf/Suggestions/FixAll/FixAllCodeFixGetFixesService.cs similarity index 97% rename from src/EditorFeatures/Core.Wpf/Suggestions/FixAll/FixAllGetFixesService.cs rename to src/EditorFeatures/Core.Wpf/Suggestions/FixAll/FixAllCodeFixGetFixesService.cs index c00c7499b38a3..617a77c56f961 100644 --- a/src/EditorFeatures/Core.Wpf/Suggestions/FixAll/FixAllGetFixesService.cs +++ b/src/EditorFeatures/Core.Wpf/Suggestions/FixAll/FixAllCodeFixGetFixesService.cs @@ -20,12 +20,12 @@ namespace Microsoft.CodeAnalysis.Editor.Implementation.Suggestions { - [ExportWorkspaceServiceFactory(typeof(IFixAllGetFixesService), ServiceLayer.Host), Shared] - internal class FixAllGetFixesService : IFixAllGetFixesService, IWorkspaceServiceFactory + [ExportWorkspaceServiceFactory(typeof(IFixAllCodeFixGetFixesService), ServiceLayer.Host), Shared] + internal class FixAllCodeFixGetFixesService : IFixAllCodeFixGetFixesService, IWorkspaceServiceFactory { [ImportingConstructor] [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] - public FixAllGetFixesService() + public FixAllCodeFixGetFixesService() { } diff --git a/src/EditorFeatures/Core.Wpf/Suggestions/FixAll/FixAllCodeRefactoringGetFixesService.cs b/src/EditorFeatures/Core.Wpf/Suggestions/FixAll/FixAllCodeRefactoringGetFixesService.cs new file mode 100644 index 0000000000000..5d20645e1f3e4 --- /dev/null +++ b/src/EditorFeatures/Core.Wpf/Suggestions/FixAll/FixAllCodeRefactoringGetFixesService.cs @@ -0,0 +1,220 @@ +// 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.Collections.Immutable; +using System.Composition; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.CodeActions; +using Microsoft.CodeAnalysis.CodeRefactorings; +using Microsoft.CodeAnalysis.Editor.Host; +using Microsoft.CodeAnalysis.Host; +using Microsoft.CodeAnalysis.Host.Mef; +using Microsoft.CodeAnalysis.Internal.Log; +using Microsoft.CodeAnalysis.PooledObjects; + +namespace Microsoft.CodeAnalysis.Editor.Implementation.Suggestions +{ + [ExportWorkspaceServiceFactory(typeof(IFixAllCodeRefactoringGetFixesService), ServiceLayer.Host), Shared] + internal class FixAllCodeRefactoringGetFixesService : IFixAllCodeRefactoringGetFixesService, IWorkspaceServiceFactory + { + [ImportingConstructor] + [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] + public FixAllCodeRefactoringGetFixesService() + { + } + + public IWorkspaceService CreateService(HostWorkspaceServices workspaceServices) + => this; + + public async Task GetFixAllChangedSolutionAsync(FixAllContext fixAllContext) + { + var codeAction = await GetFixAllCodeActionAsync(fixAllContext).ConfigureAwait(false); + if (codeAction == null) + { + return fixAllContext.Project.Solution; + } + + fixAllContext.CancellationToken.ThrowIfCancellationRequested(); + return await codeAction.GetChangedSolutionInternalAsync(cancellationToken: fixAllContext.CancellationToken).ConfigureAwait(false); + } + + public async Task> GetFixAllOperationsAsync(FixAllContext fixAllContext) + { + var codeAction = await GetFixAllCodeActionAsync(fixAllContext).ConfigureAwait(false); + if (codeAction == null) + { + return ImmutableArray.Empty; + } + + return await GetFixAllOperationsAsync( + codeAction, fixAllContext.State, fixAllContext.CancellationToken).ConfigureAwait(false); + } + + private static async Task GetFixAllCodeActionAsync(FixAllContext fixAllContext) + { + using (Logger.LogBlock( + FunctionId.CodeFixes_FixAllOccurrencesComputation, + KeyValueLogMessage.Create(LogType.UserAction, m => + { + m[FixAllLogger.CorrelationId] = fixAllContext.State.CorrelationId; + m[FixAllLogger.FixAllScope] = fixAllContext.State.FixAllScope.ToString(); + }), + fixAllContext.CancellationToken)) + { + CodeAction action = null; + try + { + action = await fixAllContext.FixAllProvider.GetFixAsync(fixAllContext).ConfigureAwait(false); + } + catch (OperationCanceledException) + { + FixAllLogger.LogComputationResult(fixAllContext.State.CorrelationId, completed: false); + } + finally + { + if (action != null) + { + FixAllLogger.LogComputationResult(fixAllContext.State.CorrelationId, completed: true); + } + else + { + FixAllLogger.LogComputationResult(fixAllContext.State.CorrelationId, completed: false, timedOut: true); + } + } + + return action; + } + } + + private static async Task> GetFixAllOperationsAsync( + CodeAction codeAction, + FixAllState fixAllState, + CancellationToken cancellationToken) + { + // We have computed the fix all occurrences code fix. + // Now fetch the new solution with applied fix and bring up the Preview changes dialog. + + var workspace = fixAllState.Project.Solution.Workspace; + + cancellationToken.ThrowIfCancellationRequested(); + var operations = await codeAction.GetOperationsAsync(cancellationToken).ConfigureAwait(false); + if (operations == null) + { + return ImmutableArray.Empty; + } + + cancellationToken.ThrowIfCancellationRequested(); + var newSolution = await codeAction.GetChangedSolutionInternalAsync(cancellationToken: cancellationToken).ConfigureAwait(false); + + newSolution = PreviewChanges( + fixAllState.Project.Solution, + newSolution, + FeaturesResources.Fix_all_occurrences, + codeAction.Title, + fixAllState.Project.Language, + workspace, + fixAllState.CorrelationId, + cancellationToken); + if (newSolution == null) + { + return ImmutableArray.Empty; + } + + // Get a code action, with apply changes operation replaced with the newSolution. + return GetNewFixAllOperations(operations, newSolution, cancellationToken); + } + + internal static Solution PreviewChanges( + Solution currentSolution, + Solution newSolution, + string fixAllPreviewChangesTitle, + string fixAllTopLevelHeader, + string languageOpt, + Workspace workspace, + int? correlationId = null, + CancellationToken cancellationToken = default) + { + cancellationToken.ThrowIfCancellationRequested(); + using (Logger.LogBlock( + FunctionId.Refactoring_FixAllOccurrencesPreviewChanges, + KeyValueLogMessage.Create(LogType.UserAction, m => + { + // only set when correlation id is given + // we might not have this info for suppression + if (correlationId.HasValue) + { + m[FixAllLogger.CorrelationId] = correlationId; + } + }), + cancellationToken)) + { + var glyph = languageOpt == null + ? Glyph.Assembly + : languageOpt == LanguageNames.CSharp + ? Glyph.CSharpProject + : Glyph.BasicProject; +#if COCOA + + var previewService = workspace.Services.GetService(); + + // Until IPreviewDialogService is implemented, just execute all changes without user ability to pick and choose + if (previewService == null) + return newSolution; +#else + + var previewService = workspace.Services.GetRequiredService(); + +#endif + + var changedSolution = previewService.PreviewChanges( + string.Format(EditorFeaturesResources.Preview_Changes_0, fixAllPreviewChangesTitle), + "vs.coderefactoring.fixall", + fixAllTopLevelHeader, + fixAllPreviewChangesTitle, + glyph, + newSolution, + currentSolution); + + if (changedSolution == null) + { + // User clicked cancel. + FixAllLogger.LogPreviewChangesResult(correlationId, applied: false); + return null; + } + + FixAllLogger.LogPreviewChangesResult(correlationId, applied: true, allChangesApplied: changedSolution == newSolution); + return changedSolution; + } + } + + private static ImmutableArray GetNewFixAllOperations(ImmutableArray operations, Solution newSolution, CancellationToken cancellationToken) + { + var result = ArrayBuilder.GetInstance(); + var foundApplyChanges = false; + foreach (var operation in operations) + { + cancellationToken.ThrowIfCancellationRequested(); + + if (!foundApplyChanges) + { + if (operation is ApplyChangesOperation) + { + foundApplyChanges = true; + result.Add(new ApplyChangesOperation(newSolution)); + continue; + } + } + + result.Add(operation); + } + + return result.ToImmutableAndFree(); + } + } +} diff --git a/src/EditorFeatures/Core.Wpf/Suggestions/SuggestedActionWithNestedFlavors.cs b/src/EditorFeatures/Core.Wpf/Suggestions/SuggestedActionWithNestedFlavors.cs index e99140f804ffb..51f5466d78777 100644 --- a/src/EditorFeatures/Core.Wpf/Suggestions/SuggestedActionWithNestedFlavors.cs +++ b/src/EditorFeatures/Core.Wpf/Suggestions/SuggestedActionWithNestedFlavors.cs @@ -26,7 +26,7 @@ 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). + /// (for refactorings and fixes). /// /// Because all derivations support 'preview changes', we bake that logic into this base type. /// @@ -40,7 +40,7 @@ public SuggestedActionWithNestedFlavors( SuggestedActionsSourceProvider sourceProvider, Workspace workspace, ITextBuffer subjectBuffer, object provider, CodeAction codeAction, - SuggestedActionSet additionalFlavors = null) + SuggestedActionSet additionalFlavors) : base(threadingContext, sourceProvider, workspace, subjectBuffer, provider, codeAction) { diff --git a/src/EditorFeatures/Core.Wpf/Suggestions/SuggestedActions/CodeRefactoringSuggestedAction.cs b/src/EditorFeatures/Core.Wpf/Suggestions/SuggestedActions/CodeRefactoringSuggestedAction.cs index 1770c5ec683f5..ac14b9c18cd06 100644 --- a/src/EditorFeatures/Core.Wpf/Suggestions/SuggestedActions/CodeRefactoringSuggestedAction.cs +++ b/src/EditorFeatures/Core.Wpf/Suggestions/SuggestedActions/CodeRefactoringSuggestedAction.cs @@ -8,6 +8,7 @@ using Microsoft.CodeAnalysis.CodeRefactorings; using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.CodeAnalysis.UnifiedSuggestions.UnifiedSuggestedActions; +using Microsoft.VisualStudio.Language.Intellisense; using Microsoft.VisualStudio.Text; namespace Microsoft.CodeAnalysis.Editor.Implementation.Suggestions @@ -25,8 +26,9 @@ public CodeRefactoringSuggestedAction( Workspace workspace, ITextBuffer subjectBuffer, CodeRefactoringProvider provider, - CodeAction codeAction) - : base(threadingContext, sourceProvider, workspace, subjectBuffer, provider, codeAction) + CodeAction codeAction, + SuggestedActionSet fixAllFlavors) + : base(threadingContext, sourceProvider, workspace, subjectBuffer, provider, codeAction, fixAllFlavors) { CodeRefactoringProvider = provider; } diff --git a/src/EditorFeatures/Core.Wpf/Suggestions/SuggestedActions/FixAllSuggestedAction.FixAllCodeAction.cs b/src/EditorFeatures/Core.Wpf/Suggestions/SuggestedActions/FixAllCodeFixSuggestedAction.FixAllCodeAction.cs similarity index 95% rename from src/EditorFeatures/Core.Wpf/Suggestions/SuggestedActions/FixAllSuggestedAction.FixAllCodeAction.cs rename to src/EditorFeatures/Core.Wpf/Suggestions/SuggestedActions/FixAllCodeFixSuggestedAction.FixAllCodeAction.cs index 266a04349b341..dc346586c0066 100644 --- a/src/EditorFeatures/Core.Wpf/Suggestions/SuggestedActions/FixAllSuggestedAction.FixAllCodeAction.cs +++ b/src/EditorFeatures/Core.Wpf/Suggestions/SuggestedActions/FixAllCodeFixSuggestedAction.FixAllCodeAction.cs @@ -9,7 +9,7 @@ namespace Microsoft.CodeAnalysis.Editor.Implementation.Suggestions { - internal partial class FixAllSuggestedAction + internal partial class FixAllCodeFixSuggestedAction { private sealed partial class FixAllCodeAction : FixSomeCodeAction { diff --git a/src/EditorFeatures/Core.Wpf/Suggestions/SuggestedActions/FixAllSuggestedAction.cs b/src/EditorFeatures/Core.Wpf/Suggestions/SuggestedActions/FixAllCodeFixSuggestedAction.cs similarity index 94% rename from src/EditorFeatures/Core.Wpf/Suggestions/SuggestedActions/FixAllSuggestedAction.cs rename to src/EditorFeatures/Core.Wpf/Suggestions/SuggestedActions/FixAllCodeFixSuggestedAction.cs index 4450286298856..a25ea4662e734 100644 --- a/src/EditorFeatures/Core.Wpf/Suggestions/SuggestedActions/FixAllSuggestedAction.cs +++ b/src/EditorFeatures/Core.Wpf/Suggestions/SuggestedActions/FixAllCodeFixSuggestedAction.cs @@ -23,7 +23,7 @@ namespace Microsoft.CodeAnalysis.Editor.Implementation.Suggestions /// Suggested action for fix all occurrences code fix. Note: this is only used /// as a 'flavor' inside CodeFixSuggestionAction. /// - internal sealed partial class FixAllSuggestedAction : SuggestedAction, ITelemetryDiagnosticID, IFixAllSuggestedAction + internal sealed partial class FixAllCodeFixSuggestedAction : SuggestedAction, ITelemetryDiagnosticID, IFixAllSuggestedAction { public Diagnostic Diagnostic { get; } @@ -37,7 +37,7 @@ internal sealed partial class FixAllSuggestedAction : SuggestedAction, ITelemetr public FixAllState FixAllState { get; } - internal FixAllSuggestedAction( + internal FixAllCodeFixSuggestedAction( IThreadingContext threadingContext, SuggestedActionsSourceProvider sourceProvider, Workspace workspace, diff --git a/src/EditorFeatures/Core.Wpf/Suggestions/SuggestedActions/FixAllCodeRefactoringSuggestedAction.cs b/src/EditorFeatures/Core.Wpf/Suggestions/SuggestedActions/FixAllCodeRefactoringSuggestedAction.cs new file mode 100644 index 0000000000000..8c9ced82d2004 --- /dev/null +++ b/src/EditorFeatures/Core.Wpf/Suggestions/SuggestedActions/FixAllCodeRefactoringSuggestedAction.cs @@ -0,0 +1,71 @@ +// 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.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.CodeActions; +using Microsoft.CodeAnalysis.CodeRefactorings; +using Microsoft.CodeAnalysis.Editor.Shared.Extensions; +using Microsoft.CodeAnalysis.Editor.Shared.Utilities; +using Microsoft.CodeAnalysis.Internal.Log; +using Microsoft.CodeAnalysis.Shared.Utilities; +using Microsoft.CodeAnalysis.UnifiedSuggestions.UnifiedSuggestedActions; +using Microsoft.VisualStudio.Text; + +namespace Microsoft.CodeAnalysis.Editor.Implementation.Suggestions +{ + /// + /// Suggested action for fix all occurrences for a code refactoring. Note: this is only used + /// as a 'flavor' inside CodeRefactoringSuggestionAction. + /// + internal sealed class FixAllCodeRefactoringSuggestedAction : SuggestedAction, IFixAllCodeRefactoringSuggestedAction + { + /// + /// The original code-action that we are a fix-all for. i.e. _originalCodeAction + /// would be something like "use 'var' instead of 'int'", this suggestion action + /// and our is the actual action that + /// will perform the fix in the appropriate document/project/solution scope. + /// + public CodeAction OriginalCodeAction { get; } + + public FixAllState FixAllState { get; } + + internal FixAllCodeRefactoringSuggestedAction( + IThreadingContext threadingContext, + SuggestedActionsSourceProvider sourceProvider, + Workspace workspace, + ITextBuffer subjectBuffer, + FixAllState fixAllState, + CodeAction originalCodeAction) + : base(threadingContext, sourceProvider, workspace, subjectBuffer, + fixAllState.FixAllProvider, new FixAllCodeRefactoringCodeAction(fixAllState)) + { + OriginalCodeAction = originalCodeAction; + FixAllState = fixAllState; + } + + public override bool TryGetTelemetryId(out Guid telemetryId) + { + // We get the telemetry id for the original code action we are fixing, + // not the special 'FixAllCodeAction'. that is the .CodeAction this + // SuggestedAction is pointing at. + telemetryId = OriginalCodeAction.GetType().GetTelemetryId(FixAllState.FixAllScope.GetScopeIdForTelemetry()); + return true; + } + + protected override async Task InnerInvokeAsync( + IProgressTracker progressTracker, CancellationToken cancellationToken) + { + await this.ThreadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); + + using (Logger.LogBlock(FunctionId.CodeFixes_FixAllOccurrencesSession, FixAllLogger.CreateCorrelationLogMessage(FixAllState.CorrelationId), cancellationToken)) + { + await base.InnerInvokeAsync(progressTracker, cancellationToken).ConfigureAwait(false); + } + } + } +} diff --git a/src/EditorFeatures/Core.Wpf/Suggestions/SuggestedActionsSource.cs b/src/EditorFeatures/Core.Wpf/Suggestions/SuggestedActionsSource.cs index 0d2a67374b859..db5383257ec24 100644 --- a/src/EditorFeatures/Core.Wpf/Suggestions/SuggestedActionsSource.cs +++ b/src/EditorFeatures/Core.Wpf/Suggestions/SuggestedActionsSource.cs @@ -235,10 +235,14 @@ ISuggestedAction ConvertToSuggestedAction(IUnifiedSuggestedAction unifiedSuggest ConvertToSuggestedActionSet(codeFixAction.FixAllFlavors, owner, subjectBuffer)), UnifiedCodeRefactoringSuggestedAction codeRefactoringAction => new CodeRefactoringSuggestedAction( ThreadingContext, owner, codeRefactoringAction.Workspace, subjectBuffer, - codeRefactoringAction.CodeRefactoringProvider, codeRefactoringAction.OriginalCodeAction), - UnifiedFixAllSuggestedAction fixAllAction => new FixAllSuggestedAction( + codeRefactoringAction.CodeRefactoringProvider, codeRefactoringAction.OriginalCodeAction, + ConvertToSuggestedActionSet(codeRefactoringAction.FixAllFlavors, owner, subjectBuffer)), + UnifiedFixAllSuggestedAction fixAllAction => new FixAllCodeFixSuggestedAction( ThreadingContext, owner, fixAllAction.Workspace, subjectBuffer, fixAllAction.FixAllState, fixAllAction.Diagnostic, fixAllAction.OriginalCodeAction), + UnifiedFixAllCodeRefactoringSuggestedAction fixAllCodeRefactoringAction => new FixAllCodeRefactoringSuggestedAction( + ThreadingContext, owner, fixAllCodeRefactoringAction.Workspace, subjectBuffer, + fixAllCodeRefactoringAction.FixAllState, fixAllCodeRefactoringAction.OriginalCodeAction), UnifiedSuggestedActionWithNestedActions nestedAction => new SuggestedActionWithNestedActions( ThreadingContext, owner, nestedAction.Workspace, subjectBuffer, nestedAction.Provider ?? this, nestedAction.OriginalCodeAction, diff --git a/src/EditorFeatures/Core/Implementation/EditorLayerExtensionManager.cs b/src/EditorFeatures/Core/Implementation/EditorLayerExtensionManager.cs index 0a4e0d733d82e..757e547640b55 100644 --- a/src/EditorFeatures/Core/Implementation/EditorLayerExtensionManager.cs +++ b/src/EditorFeatures/Core/Implementation/EditorLayerExtensionManager.cs @@ -68,7 +68,7 @@ public ExtensionManager( public override void HandleException(object provider, Exception exception) { - if (provider is CodeFixProvider or FixAllProvider or CodeRefactoringProvider) + if (provider is CodeFixProvider or CodeFixes.FixAllProvider or CodeRefactoringProvider or CodeRefactorings.FixAllProvider) { if (!IsIgnored(provider) && _globalOptions.GetOption(ExtensionManagerOptions.DisableCrashingExtensions)) diff --git a/src/EditorFeatures/Core/Shared/Extensions/TelemetryExtensions.cs b/src/EditorFeatures/Core/Shared/Extensions/TelemetryExtensions.cs index c9ca4ce684aa9..3e3fcbf1f93c4 100644 --- a/src/EditorFeatures/Core/Shared/Extensions/TelemetryExtensions.cs +++ b/src/EditorFeatures/Core/Shared/Extensions/TelemetryExtensions.cs @@ -43,6 +43,18 @@ public static short GetScopeIdForTelemetry(this FixAllScope scope) _ => 4, }; + public static short GetScopeIdForTelemetry(this CodeRefactorings.FixAllScope scope) + => scope switch + { + CodeRefactorings.FixAllScope.Document => 1, + CodeRefactorings.FixAllScope.Project => 2, + CodeRefactorings.FixAllScope.Solution => 3, + CodeRefactorings.FixAllScope.Selection => 4, + CodeRefactorings.FixAllScope.ContainingMember => 5, + CodeRefactorings.FixAllScope.ContainingType => 6, + _ => short.MaxValue, + }; + public static string GetTelemetryDiagnosticID(this Diagnostic diagnostic) { // we log diagnostic id as it is if it is from us From 4630883944315a0f9eb2e3b0d43c864acc2a33f6 Mon Sep 17 00:00:00 2001 From: Manish Vasani Date: Mon, 28 Feb 2022 06:49:52 -0800 Subject: [PATCH 05/18] VisualStudio layer changes for FixAll support for refactorings --- .../Suppression/VisualStudioSuppressionFixService.cs | 2 +- .../New.IntegrationTests/InProcess/EditorInProcess.cs | 6 +++--- .../TestUtilities/InProcess/TextViewWindow_InProc.cs | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/VisualStudio/Core/Def/Implementation/TableDataSource/Suppression/VisualStudioSuppressionFixService.cs b/src/VisualStudio/Core/Def/Implementation/TableDataSource/Suppression/VisualStudioSuppressionFixService.cs index 04908186f14ae..a901026cfcd26 100644 --- a/src/VisualStudio/Core/Def/Implementation/TableDataSource/Suppression/VisualStudioSuppressionFixService.cs +++ b/src/VisualStudio/Core/Def/Implementation/TableDataSource/Suppression/VisualStudioSuppressionFixService.cs @@ -350,7 +350,7 @@ private async Task ApplySuppressionFixAsync(IEnumerable? diagnos if (showPreviewChangesDialog) { - newSolution = FixAllGetFixesService.PreviewChanges( + newSolution = FixAllCodeFixGetFixesService.PreviewChanges( _workspace.CurrentSolution, newSolution, fixAllPreviewChangesTitle: title, diff --git a/src/VisualStudio/IntegrationTest/New.IntegrationTests/InProcess/EditorInProcess.cs b/src/VisualStudio/IntegrationTest/New.IntegrationTests/InProcess/EditorInProcess.cs index bdbf90e0f1669..b4d9558fb2065 100644 --- a/src/VisualStudio/IntegrationTest/New.IntegrationTests/InProcess/EditorInProcess.cs +++ b/src/VisualStudio/IntegrationTest/New.IntegrationTests/InProcess/EditorInProcess.cs @@ -668,7 +668,7 @@ private Func> GetLightBulbApplicatio action = fixAllAction; if (willBlockUntilComplete - && action is FixAllSuggestedAction fixAllSuggestedAction + && action is FixAllCodeFixSuggestedAction fixAllSuggestedAction && fixAllSuggestedAction.CodeAction is FixSomeCodeAction fixSomeCodeAction) { // Ensure the preview changes dialog will not be shown. Since the operation 'willBlockUntilComplete', @@ -756,7 +756,7 @@ private async Task> SelectActionsAsync(IEnumerable return actions; } - private async Task GetFixAllSuggestedActionAsync(IEnumerable actionSets, FixAllScope fixAllScope, CancellationToken cancellationToken) + private async Task GetFixAllSuggestedActionAsync(IEnumerable actionSets, FixAllScope fixAllScope, CancellationToken cancellationToken) { await JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); @@ -764,7 +764,7 @@ private async Task> SelectActionsAsync(IEnumerable { foreach (var action in actionSet.Actions) { - if (action is FixAllSuggestedAction fixAllSuggestedAction) + if (action is FixAllCodeFixSuggestedAction fixAllSuggestedAction) { var fixAllCodeAction = fixAllSuggestedAction.CodeAction as FixSomeCodeAction; if (fixAllCodeAction?.FixAllState?.Scope == fixAllScope) diff --git a/src/VisualStudio/IntegrationTest/TestUtilities/InProcess/TextViewWindow_InProc.cs b/src/VisualStudio/IntegrationTest/TestUtilities/InProcess/TextViewWindow_InProc.cs index c40c5f1fe6b14..a74bd1e54757d 100644 --- a/src/VisualStudio/IntegrationTest/TestUtilities/InProcess/TextViewWindow_InProc.cs +++ b/src/VisualStudio/IntegrationTest/TestUtilities/InProcess/TextViewWindow_InProc.cs @@ -428,7 +428,7 @@ private Func> GetLightBulbApplicationAction(string acti action = fixAllAction; if (willBlockUntilComplete - && action is FixAllSuggestedAction fixAllSuggestedAction + && action is FixAllCodeFixSuggestedAction fixAllSuggestedAction && fixAllSuggestedAction.CodeAction is FixSomeCodeAction fixSomeCodeAction) { // Ensure the preview changes dialog will not be shown. Since the operation 'willBlockUntilComplete', @@ -495,7 +495,7 @@ private async Task> SelectActionsAsync(IEnumerable return actions; } - private static async Task GetFixAllSuggestedActionAsync(IEnumerable actionSets, FixAllScope fixAllScope) + private static async Task GetFixAllSuggestedActionAsync(IEnumerable actionSets, FixAllScope fixAllScope) { await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); @@ -503,7 +503,7 @@ private async Task> SelectActionsAsync(IEnumerable { foreach (var action in actionSet.Actions) { - if (action is FixAllSuggestedAction fixAllSuggestedAction) + if (action is FixAllCodeFixSuggestedAction fixAllSuggestedAction) { var fixAllCodeAction = fixAllSuggestedAction.CodeAction as FixSomeCodeAction; if (fixAllCodeAction?.FixAllState?.Scope == fixAllScope) From 96cd412995ed7d928e3dc318abcb0e1460c76e50 Mon Sep 17 00:00:00 2001 From: Manish Vasani Date: Mon, 28 Feb 2022 06:50:47 -0800 Subject: [PATCH 06/18] Proof of concept - "Invert conditional" code refactoring enhancement to add FixAll support --- ...nvertConditionalCodeRefactoringProvider.cs | 37 +++++++++++++++---- 1 file changed, 30 insertions(+), 7 deletions(-) diff --git a/src/Features/Core/Portable/InvertConditional/AbstractInvertConditionalCodeRefactoringProvider.cs b/src/Features/Core/Portable/InvertConditional/AbstractInvertConditionalCodeRefactoringProvider.cs index 59da6609cc87e..5881e39e2c84a 100644 --- a/src/Features/Core/Portable/InvertConditional/AbstractInvertConditionalCodeRefactoringProvider.cs +++ b/src/Features/Core/Portable/InvertConditional/AbstractInvertConditionalCodeRefactoringProvider.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System; +using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeActions; @@ -10,12 +11,11 @@ using Microsoft.CodeAnalysis.Editing; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.InvertConditional { internal abstract class AbstractInvertConditionalCodeRefactoringProvider - : CodeRefactoringProvider + : SyntaxEditorBasedCodeRefactoringProvider where TConditionalExpressionSyntax : SyntaxNode { protected abstract bool ShouldOffer(TConditionalExpressionSyntax conditional); @@ -31,7 +31,7 @@ public override async Task ComputeRefactoringsAsync(CodeRefactoringContext conte } context.RegisterRefactoring(new MyCodeAction( - c => InvertConditionalAsync(document, span, c)), + c => InvertConditionalAsync(document, conditional, c)), conditional.Span); } @@ -39,12 +39,35 @@ public override async Task ComputeRefactoringsAsync(CodeRefactoringContext conte Document document, TextSpan span, CancellationToken cancellationToken) => await document.TryGetRelevantNodeAsync(span, cancellationToken).ConfigureAwait(false); - private static async Task InvertConditionalAsync( - Document document, TextSpan span, CancellationToken cancellationToken) + protected override async Task FixAllAsync(Document document, TextSpan fixAllSpan, CodeAction? originalCodeAction, SyntaxEditor editor, CancellationToken cancellationToken) { - var conditional = await FindConditionalAsync(document, span, cancellationToken).ConfigureAwait(false); - Contract.ThrowIfNull(conditional); + var originalRoot = editor.OriginalRoot; + + // Get all conditional nodes in the given fixAllSpan. + var conditionals = originalRoot.DescendantNodes(node => fixAllSpan.IntersectsWith(node.Span)).OfType(); + + // We're going to be continually editing this tree. Track all the nodes we + // care about so we can find them across each edit. + document = document.WithSyntaxRoot(originalRoot.TrackNodes(conditionals)); + + foreach (var originalConditional in conditionals.Reverse()) + { + // Only process conditionals fully within fixAllSpan + if (!fixAllSpan.Contains(originalConditional.Span)) + continue; + var currentRoot = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); + var currentConditional = currentRoot.GetCurrentNodes(originalConditional).Single(); + document = await InvertConditionalAsync(document, currentConditional, cancellationToken).ConfigureAwait(false); + } + + var root = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); + editor.ReplaceNode(originalRoot, root); + } + + private static async Task InvertConditionalAsync( + Document document, TConditionalExpressionSyntax conditional, CancellationToken cancellationToken) + { var semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); var root = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); From c1adefdd0b2fa5d85d9e22ebd86acb6e75eb4e39 Mon Sep 17 00:00:00 2001 From: Manish Vasani Date: Mon, 11 Apr 2022 02:50:07 -0700 Subject: [PATCH 07/18] Remove FixAllScope.Selection --- .../FixAllCodeRefactoringCodeAction.cs | 1 - .../Core/Portable/FeaturesResources.resx | 3 --- .../Core/Portable/xlf/FeaturesResources.cs.xlf | 5 ----- .../Core/Portable/xlf/FeaturesResources.de.xlf | 5 ----- .../Core/Portable/xlf/FeaturesResources.es.xlf | 5 ----- .../Core/Portable/xlf/FeaturesResources.fr.xlf | 5 ----- .../Core/Portable/xlf/FeaturesResources.it.xlf | 5 ----- .../Core/Portable/xlf/FeaturesResources.ja.xlf | 5 ----- .../Core/Portable/xlf/FeaturesResources.ko.xlf | 5 ----- .../Core/Portable/xlf/FeaturesResources.pl.xlf | 5 ----- .../Portable/xlf/FeaturesResources.pt-BR.xlf | 5 ----- .../Core/Portable/xlf/FeaturesResources.ru.xlf | 5 ----- .../Core/Portable/xlf/FeaturesResources.tr.xlf | 5 ----- .../Portable/xlf/FeaturesResources.zh-Hans.xlf | 5 ----- .../Portable/xlf/FeaturesResources.zh-Hant.xlf | 5 ----- ...edActionsSource.CodeRefactoringsComputer.cs | 4 ---- .../DocumentBasedFixAllProvider.cs | 18 ++++-------------- .../FixAllOccurences/FixAllContext.cs | 5 ++--- .../FixAllOccurences/FixAllContextHelper.cs | 1 - .../FixAllOccurences/FixAllProvider.cs | 8 +------- .../FixAllOccurences/FixAllScope.cs | 1 - .../Core/Portable/PublicAPI.Unshipped.txt | 2 +- .../Shared/Extensions/TelemetryExtensions.cs | 5 ++--- .../CodeRefactorings/FixAll/FixAllState.cs | 4 ++-- ...SyntaxEditorBasedCodeRefactoringProvider.cs | 5 ----- .../Core/WorkspaceExtensionsResources.resx | 3 --- .../xlf/WorkspaceExtensionsResources.cs.xlf | 5 ----- .../xlf/WorkspaceExtensionsResources.de.xlf | 5 ----- .../xlf/WorkspaceExtensionsResources.es.xlf | 5 ----- .../xlf/WorkspaceExtensionsResources.fr.xlf | 5 ----- .../xlf/WorkspaceExtensionsResources.it.xlf | 5 ----- .../xlf/WorkspaceExtensionsResources.ja.xlf | 5 ----- .../xlf/WorkspaceExtensionsResources.ko.xlf | 5 ----- .../xlf/WorkspaceExtensionsResources.pl.xlf | 5 ----- .../xlf/WorkspaceExtensionsResources.pt-BR.xlf | 5 ----- .../xlf/WorkspaceExtensionsResources.ru.xlf | 5 ----- .../xlf/WorkspaceExtensionsResources.tr.xlf | 5 ----- .../WorkspaceExtensionsResources.zh-Hans.xlf | 5 ----- .../WorkspaceExtensionsResources.zh-Hant.xlf | 5 ----- 39 files changed, 12 insertions(+), 178 deletions(-) diff --git a/src/Features/Core/Portable/CodeRefactorings/FixAllOccurences/FixAllCodeRefactoringCodeAction.cs b/src/Features/Core/Portable/CodeRefactorings/FixAllOccurences/FixAllCodeRefactoringCodeAction.cs index a951a7537dc06..7a7d1feb8fbec 100644 --- a/src/Features/Core/Portable/CodeRefactorings/FixAllOccurences/FixAllCodeRefactoringCodeAction.cs +++ b/src/Features/Core/Portable/CodeRefactorings/FixAllOccurences/FixAllCodeRefactoringCodeAction.cs @@ -29,7 +29,6 @@ public override string Title FixAllScope.Document => FeaturesResources.Document, FixAllScope.Project => FeaturesResources.Project, FixAllScope.Solution => FeaturesResources.Solution, - FixAllScope.Selection => FeaturesResources.Selection, FixAllScope.ContainingMember => FeaturesResources.Containing_Member, FixAllScope.ContainingType => FeaturesResources.Containing_Type, _ => throw new NotSupportedException(), diff --git a/src/Features/Core/Portable/FeaturesResources.resx b/src/Features/Core/Portable/FeaturesResources.resx index 98ea8ebe3ed56..f136dc94ae276 100644 --- a/src/Features/Core/Portable/FeaturesResources.resx +++ b/src/Features/Core/Portable/FeaturesResources.resx @@ -712,9 +712,6 @@ Do you want to continue? Solution - - Selection - Containing Member diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.cs.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.cs.xlf index 177509bd95c02..78e954e20b071 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.cs.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.cs.xlf @@ -2505,11 +2505,6 @@ Pozitivní kontrolní výrazy zpětného vyhledávání s nulovou délkou se obv Hrubá úprava - - Selection - Selection - - Selection does not contain a valid token. Výběr neobsahuje platný token. diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.de.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.de.xlf index 8bed688b47151..fe1f11c6beb78 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.de.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.de.xlf @@ -2505,11 +2505,6 @@ Positive Lookbehindassertionen mit Nullbreite werden normalerweise am Anfang reg Grobe Bearbeitung - - Selection - Selection - - Selection does not contain a valid token. Auswahl enthält kein gültiges Token. diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.es.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.es.xlf index bbf4d4d71927a..6b5c97aa33c04 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.es.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.es.xlf @@ -2505,11 +2505,6 @@ Las aserciones de búsqueda retrasada (lookbehind) positivas de ancho cero se us Edición superficial - - Selection - Selection - - Selection does not contain a valid token. La selección no contiene un token válido diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.fr.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.fr.xlf index 50464643097c0..4556d6cba01e2 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.fr.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.fr.xlf @@ -2505,11 +2505,6 @@ Les assertions arrière positives de largeur nulle sont généralement utilisée Modification non applicable - - Selection - Selection - - Selection does not contain a valid token. La sélection ne contient pas un jeton valide. diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.it.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.it.xlf index bc586d894eccf..d7eac79e7a648 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.it.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.it.xlf @@ -2505,11 +2505,6 @@ Le asserzioni lookbehind positive di larghezza zero vengono usate in genere all' Modifica non applicabile - - Selection - Selection - - Selection does not contain a valid token. La selezione non contiene un token valido. diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.ja.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.ja.xlf index 901be083925fe..86c5f48fe1753 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.ja.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.ja.xlf @@ -2505,11 +2505,6 @@ Zero-width positive lookbehind assertions are typically used at the beginning of Rude 編集 - - Selection - Selection - - Selection does not contain a valid token. 選択には有効なトークンは含まれません。 diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.ko.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.ko.xlf index bb6c790c3bb9f..01aa69bc51093 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.ko.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.ko.xlf @@ -2505,11 +2505,6 @@ Zero-width positive lookbehind assertions are typically used at the beginning of 편집 다시 실행 - - Selection - Selection - - Selection does not contain a valid token. 선택 항목에 유효한 토큰이 포함되어 있지 않습니다. diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.pl.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.pl.xlf index ef6ddc3a6f773..eb0509769f713 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.pl.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.pl.xlf @@ -2505,11 +2505,6 @@ Pozytywne asercje wsteczne o zerowej szerokości są zwykle używane na początk Edycja reguły - - Selection - Selection - - Selection does not contain a valid token. Zaznaczenie nie zawiera prawidłowego tokenu. diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.pt-BR.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.pt-BR.xlf index 2598635b858a0..3a0ae27d7c213 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.pt-BR.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.pt-BR.xlf @@ -2505,11 +2505,6 @@ As declarações de lookbehind positivas de largura zero normalmente são usadas Edição rudimentar - - Selection - Selection - - Selection does not contain a valid token. A seleção não contém um token válido. diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.ru.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.ru.xlf index 9535772b4322e..83cbfe2baa2ed 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.ru.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.ru.xlf @@ -2505,11 +2505,6 @@ Zero-width positive lookbehind assertions are typically used at the beginning of Грубая редакция - - Selection - Selection - - Selection does not contain a valid token. Выделение не содержит допустимый токен. diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.tr.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.tr.xlf index 6fe3c40033df3..3ad61a57a831a 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.tr.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.tr.xlf @@ -2505,11 +2505,6 @@ Sıfır genişlikli pozitif geri yönlü onaylamalar genellikle normal ifadeleri İşlenmemiş düzenleme - - Selection - Selection - - Selection does not contain a valid token. Seçim, geçerli bir belirteç içermiyor. diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hans.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hans.xlf index 5d460c4aca855..b658f2a1d7aac 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hans.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hans.xlf @@ -2505,11 +2505,6 @@ Zero-width positive lookbehind assertions are typically used at the beginning of 原始编辑 - - Selection - Selection - - Selection does not contain a valid token. 所选内容不包含有效令牌。 diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hant.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hant.xlf index 055f276a00b72..5d4ff36d0bcc3 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hant.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hant.xlf @@ -2505,11 +2505,6 @@ Zero-width positive lookbehind assertions are typically used at the beginning of 粗略編輯 - - Selection - Selection - - Selection does not contain a valid token. 選取範圍沒有包含有效的語彙基元。 diff --git a/src/Features/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActionsSource.CodeRefactoringsComputer.cs b/src/Features/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActionsSource.CodeRefactoringsComputer.cs index b760bf29d20f0..51fa8c1b34735 100644 --- a/src/Features/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActionsSource.CodeRefactoringsComputer.cs +++ b/src/Features/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActionsSource.CodeRefactoringsComputer.cs @@ -197,9 +197,6 @@ async Task GetUnifiedSuggestedActionSetAsync(CodeAction using var fixAllSuggestedActionsDisposer = ArrayBuilder.GetInstance(out var fixAllSuggestedActions); foreach (var scope in fixAllProviderInfo.SupportedScopes) { - if (scope == FixAllScope.Selection && selection.IsEmpty) - continue; - var fixAllSpan = await GetFixAllSpanForScopeAsync(scope).ConfigureAwait(false); var fixAllState = new FixAllState(fixAllProviderInfo.FixAllProvider, document, provider, scope, fixAllSpan, action); var fixAllSuggestedAction = new UnifiedFixAllCodeRefactoringSuggestedAction( @@ -220,7 +217,6 @@ async Task GetUnifiedSuggestedActionSetAsync(CodeAction { return fixAllScope switch { - FixAllScope.Selection => selection, FixAllScope.ContainingMember or FixAllScope.ContainingType => await GetSpanForContainingMemberOrTypeAsync(fixAllScope).ConfigureAwait(false), _ => null, diff --git a/src/Workspaces/Core/Portable/CodeRefactorings/FixAllOccurences/DocumentBasedFixAllProvider.cs b/src/Workspaces/Core/Portable/CodeRefactorings/FixAllOccurences/DocumentBasedFixAllProvider.cs index 824ada5c52d27..315d23025e959 100644 --- a/src/Workspaces/Core/Portable/CodeRefactorings/FixAllOccurences/DocumentBasedFixAllProvider.cs +++ b/src/Workspaces/Core/Portable/CodeRefactorings/FixAllOccurences/DocumentBasedFixAllProvider.cs @@ -55,12 +55,6 @@ protected virtual string GetFixAllTitle(FixAllContext fixAllContext) /// protected abstract Task FixAllAsync(FixAllContext fixAllContext); - /// - /// Returns a bool indicating if the provider supports FixAll in selected span, - /// i.e. - /// - protected abstract bool SupportsFixAllForSelection { get; } - /// /// Returns a bool indicating if the provider supports FixAll in containing member, /// i.e. @@ -78,9 +72,6 @@ public sealed override IEnumerable GetSupportedFixAllScopes() foreach (var defaultScope in base.GetSupportedFixAllScopes()) yield return defaultScope; - if (SupportsFixAllForSelection) - yield return FixAllScope.Selection; - if (SupportsFixAllForContainingMember) yield return FixAllScope.ContainingMember; @@ -96,12 +87,11 @@ public sealed override IEnumerable GetSupportedFixAllScopes() { Contract.ThrowIfFalse(fixAllContext.Scope is FixAllScope.Document or FixAllScope.Project or FixAllScope.Solution or - FixAllScope.Selection or FixAllScope.ContainingMember or FixAllScope.ContainingType); + FixAllScope.ContainingMember or FixAllScope.ContainingType); var solution = fixAllContext.Scope switch { - FixAllScope.Document or FixAllScope.Selection or - FixAllScope.ContainingMember or FixAllScope.ContainingType + FixAllScope.Document or FixAllScope.ContainingMember or FixAllScope.ContainingType => await GetDocumentFixesAsync(fixAllContext, fixAllContextsAsync).ConfigureAwait(false), FixAllScope.Project => await GetProjectFixesAsync(fixAllContext, fixAllContextsAsync).ConfigureAwait(false), @@ -167,7 +157,7 @@ FixAllScope.ContainingMember or FixAllScope.ContainingType foreach (var fixAllContext in fixAllContexts) { Contract.ThrowIfFalse(fixAllContext.Scope is FixAllScope.Document or FixAllScope.Project or - FixAllScope.Selection or FixAllScope.ContainingMember or FixAllScope.ContainingType); + FixAllScope.ContainingMember or FixAllScope.ContainingType); currentSolution = await FixSingleContextAsync(currentSolution, fixAllContext, progressTracker).ConfigureAwait(false); } @@ -195,7 +185,7 @@ private async Task FixSingleContextAsync(Solution currentSolution, Fix FixAllContext fixAllContext, IProgressTracker progressTracker) { Contract.ThrowIfFalse(fixAllContext.Scope is FixAllScope.Document or FixAllScope.Project - or FixAllScope.Selection or FixAllScope.ContainingMember or FixAllScope.ContainingType); + or FixAllScope.ContainingMember or FixAllScope.ContainingType); var cancellationToken = fixAllContext.CancellationToken; diff --git a/src/Workspaces/Core/Portable/CodeRefactorings/FixAllOccurences/FixAllContext.cs b/src/Workspaces/Core/Portable/CodeRefactorings/FixAllOccurences/FixAllContext.cs index 98dc304173da2..b5d168ce6a3e4 100644 --- a/src/Workspaces/Core/Portable/CodeRefactorings/FixAllOccurences/FixAllContext.cs +++ b/src/Workspaces/Core/Portable/CodeRefactorings/FixAllOccurences/FixAllContext.cs @@ -43,9 +43,8 @@ public sealed class FixAllContext /// Optional span within the to fix all occurrences. /// This span can be non-null only when is any of the following document based scopes: /// 1. - /// 2. - /// 3. - /// 4. + /// 2. + /// 3. /// public TextSpan? FixAllSpan => State.FixAllSpan; diff --git a/src/Workspaces/Core/Portable/CodeRefactorings/FixAllOccurences/FixAllContextHelper.cs b/src/Workspaces/Core/Portable/CodeRefactorings/FixAllOccurences/FixAllContextHelper.cs index 7ca7ecd1a76de..687a6d3e21161 100644 --- a/src/Workspaces/Core/Portable/CodeRefactorings/FixAllOccurences/FixAllContextHelper.cs +++ b/src/Workspaces/Core/Portable/CodeRefactorings/FixAllOccurences/FixAllContextHelper.cs @@ -26,7 +26,6 @@ public static string GetDefaultFixAllTitle( FixAllScope.Document => string.Format(WorkspaceExtensionsResources.Fix_all_0_in_1, title, triggerDocument!.Name), FixAllScope.Project => string.Format(WorkspaceExtensionsResources.Fix_all_0_in_1, title, triggerProject.Name), FixAllScope.Solution => string.Format(WorkspaceExtensionsResources.Fix_all_0_in_Solution, title), - FixAllScope.Selection => string.Format(WorkspaceExtensionsResources.Fix_all_0_in_selection_for_1, title, triggerDocument!.Name), FixAllScope.ContainingMember => string.Format(WorkspaceExtensionsResources.Fix_all_0_in_containing_member_for_1, title, triggerDocument!.Name), FixAllScope.ContainingType => string.Format(WorkspaceExtensionsResources.Fix_all_0_in_containing_type_for_1, title, triggerDocument!.Name), _ => throw ExceptionUtilities.UnexpectedValue(fixAllScope), diff --git a/src/Workspaces/Core/Portable/CodeRefactorings/FixAllOccurences/FixAllProvider.cs b/src/Workspaces/Core/Portable/CodeRefactorings/FixAllOccurences/FixAllProvider.cs index 8bc6c6a226439..2542a61478de6 100644 --- a/src/Workspaces/Core/Portable/CodeRefactorings/FixAllOccurences/FixAllProvider.cs +++ b/src/Workspaces/Core/Portable/CodeRefactorings/FixAllOccurences/FixAllProvider.cs @@ -43,12 +43,10 @@ public virtual IEnumerable GetSupportedFixAllScopes() /// of it (like attributes), or changes to the or it points at /// will be considered. /// - /// Indicates if is supported or not. /// Indicates if is supported or not. /// Indicates if is supported or not. public static FixAllProvider Create( Func> fixAllAsync, - bool supportsFixAllForSelection, bool supportsFixAllForContainingMember, bool supportsFixAllForContainingType) { @@ -56,7 +54,7 @@ public static FixAllProvider Create( throw new ArgumentNullException(nameof(fixAllAsync)); return new CallbackDocumentBasedFixAllProvider(fixAllAsync, - supportsFixAllForSelection, supportsFixAllForContainingMember, supportsFixAllForContainingType); + supportsFixAllForContainingMember, supportsFixAllForContainingType); } private sealed class CallbackDocumentBasedFixAllProvider : DocumentBasedFixAllProvider @@ -65,18 +63,14 @@ private sealed class CallbackDocumentBasedFixAllProvider : DocumentBasedFixAllPr public CallbackDocumentBasedFixAllProvider( Func> fixAllAsync, - bool supportsFixAllForSelection, bool supportsFixAllForContainingMember, bool supportsFixAllForContainingType) { _fixAllAsync = fixAllAsync; - SupportsFixAllForSelection = supportsFixAllForSelection; SupportsFixAllForContainingMember = supportsFixAllForContainingMember; SupportsFixAllForContainingType = supportsFixAllForContainingType; } - protected override bool SupportsFixAllForSelection { get; } - protected override bool SupportsFixAllForContainingMember { get; } protected override bool SupportsFixAllForContainingType { get; } diff --git a/src/Workspaces/Core/Portable/CodeRefactorings/FixAllOccurences/FixAllScope.cs b/src/Workspaces/Core/Portable/CodeRefactorings/FixAllOccurences/FixAllScope.cs index bc7d71bcdfe65..8fcb0e1748cc8 100644 --- a/src/Workspaces/Core/Portable/CodeRefactorings/FixAllOccurences/FixAllScope.cs +++ b/src/Workspaces/Core/Portable/CodeRefactorings/FixAllOccurences/FixAllScope.cs @@ -12,7 +12,6 @@ public enum FixAllScope Document, Project, Solution, - Selection, ContainingMember, ContainingType, diff --git a/src/Workspaces/Core/Portable/PublicAPI.Unshipped.txt b/src/Workspaces/Core/Portable/PublicAPI.Unshipped.txt index 51e0b76202536..b8d584acf569e 100644 --- a/src/Workspaces/Core/Portable/PublicAPI.Unshipped.txt +++ b/src/Workspaces/Core/Portable/PublicAPI.Unshipped.txt @@ -32,7 +32,7 @@ Microsoft.CodeAnalysis.Editing.SyntaxEditor.SyntaxEditor(Microsoft.CodeAnalysis. static Microsoft.CodeAnalysis.CodeFixes.FixAllProvider.Create(System.Func, System.Threading.Tasks.Task> fixAllAsync, System.Collections.Immutable.ImmutableArray supportedFixAllScopes) -> Microsoft.CodeAnalysis.CodeFixes.FixAllProvider override sealed Microsoft.CodeAnalysis.CodeRefactorings.DocumentBasedFixAllProvider.GetFixAsync(Microsoft.CodeAnalysis.CodeRefactorings.FixAllContext fixAllContext) -> System.Threading.Tasks.Task override sealed Microsoft.CodeAnalysis.CodeRefactorings.DocumentBasedFixAllProvider.GetSupportedFixAllScopes() -> System.Collections.Generic.IEnumerable -static Microsoft.CodeAnalysis.CodeRefactorings.FixAllProvider.Create(System.Func> fixAllAsync, bool supportsFixAllForSelection, bool supportsFixAllForContainingMember, bool supportsFixAllForContainingType) -> Microsoft.CodeAnalysis.CodeRefactorings.FixAllProvider +static Microsoft.CodeAnalysis.CodeRefactorings.FixAllProvider.Create(System.Func> fixAllAsync, bool supportsFixAllForContainingMember, bool supportsFixAllForContainingType) -> Microsoft.CodeAnalysis.CodeRefactorings.FixAllProvider static readonly Microsoft.CodeAnalysis.Editing.SyntaxGenerator.DefaultRemoveOptions -> Microsoft.CodeAnalysis.SyntaxRemoveOptions Microsoft.CodeAnalysis.Rename.SymbolRenameOptions Microsoft.CodeAnalysis.Rename.SymbolRenameOptions.SymbolRenameOptions() -> void diff --git a/src/Workspaces/Core/Portable/Shared/Extensions/TelemetryExtensions.cs b/src/Workspaces/Core/Portable/Shared/Extensions/TelemetryExtensions.cs index 26ededcc6bcec..d7e4cb2c71fba 100644 --- a/src/Workspaces/Core/Portable/Shared/Extensions/TelemetryExtensions.cs +++ b/src/Workspaces/Core/Portable/Shared/Extensions/TelemetryExtensions.cs @@ -56,9 +56,8 @@ public static short GetScopeIdForTelemetry(this CodeRefactorings.FixAllScope sco CodeRefactorings.FixAllScope.Document => 1, CodeRefactorings.FixAllScope.Project => 2, CodeRefactorings.FixAllScope.Solution => 3, - CodeRefactorings.FixAllScope.Selection => 4, - CodeRefactorings.FixAllScope.ContainingMember => 5, - CodeRefactorings.FixAllScope.ContainingType => 6, + CodeRefactorings.FixAllScope.ContainingMember => 4, + CodeRefactorings.FixAllScope.ContainingType => 5, _ => short.MaxValue, }; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeRefactorings/FixAll/FixAllState.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeRefactorings/FixAll/FixAllState.cs index 764971374118e..96f763b7a134d 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeRefactorings/FixAll/FixAllState.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeRefactorings/FixAll/FixAllState.cs @@ -30,7 +30,7 @@ internal FixAllState( FixAllScope fixAllScope, TextSpan? fixAllSpan, CodeAction codeAction) - : this(fixAllProvider, document, document.Project, codeRefactoringProvider, fixAllScope, fixAllSpan, codeAction) + : this(fixAllProvider, document, document.Project, codeRefactoringProvider, fixAllScope, fixAllSpan, codeAction) { if (document == null) { @@ -63,7 +63,7 @@ private FixAllState( { Contract.ThrowIfNull(project); Contract.ThrowIfFalse(!fixAllSpan.HasValue || fixAllScope is FixAllScope.Document or - FixAllScope.Selection or FixAllScope.ContainingMember or FixAllScope.ContainingType); + FixAllScope.ContainingMember or FixAllScope.ContainingType); this.FixAllProvider = fixAllProvider; this.Document = document; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeRefactorings/SyntaxEditorBasedCodeRefactoringProvider.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeRefactorings/SyntaxEditorBasedCodeRefactoringProvider.cs index cb236fb281c6a..6ed16a8cbe7ad 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeRefactorings/SyntaxEditorBasedCodeRefactoringProvider.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeRefactorings/SyntaxEditorBasedCodeRefactoringProvider.cs @@ -16,23 +16,19 @@ namespace Microsoft.CodeAnalysis.CodeRefactorings internal abstract partial class SyntaxEditorBasedCodeRefactoringProvider : CodeRefactoringProvider { private readonly bool _supportsFixAll; - private readonly bool _supportsFixAllForSelection; private readonly bool _supportsFixAllForContainingMember; private readonly bool _supportsFixAllForContainingType; protected SyntaxEditorBasedCodeRefactoringProvider( bool supportsFixAll = true, - bool supportsFixAllForSelection = true, bool supportsFixAllForContainingMember = true, bool supportsFixAllForContainingType = true) { _supportsFixAll = supportsFixAll; - _supportsFixAllForSelection = supportsFixAllForSelection; _supportsFixAllForContainingMember = supportsFixAllForContainingMember; _supportsFixAllForContainingType = supportsFixAllForContainingType; } - public sealed override FixAllProvider? GetFixAllProvider() { if (!_supportsFixAll) @@ -43,7 +39,6 @@ protected SyntaxEditorBasedCodeRefactoringProvider( { return await this.FixAllAsync(fixAllContext.Document, fixAllContext.FixAllSpan, fixAllContext.CodeAction, fixAllContext.CancellationToken).ConfigureAwait(false); }, - _supportsFixAllForSelection, _supportsFixAllForContainingMember, _supportsFixAllForContainingType); } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/WorkspaceExtensionsResources.resx b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/WorkspaceExtensionsResources.resx index c173eda1cdc50..682f92c5f1c0a 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/WorkspaceExtensionsResources.resx +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/WorkspaceExtensionsResources.resx @@ -126,9 +126,6 @@ Fix all '{0}' in '{1}' - - Fix all '{0}' in selection for '{1}' - Fix all '{0}' in containing member for '{1}' diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/xlf/WorkspaceExtensionsResources.cs.xlf b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/xlf/WorkspaceExtensionsResources.cs.xlf index 57f8e7b6d6f24..945fe766758b3 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/xlf/WorkspaceExtensionsResources.cs.xlf +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/xlf/WorkspaceExtensionsResources.cs.xlf @@ -42,11 +42,6 @@ Fix all '{0}' in containing type for '{1}' - - Fix all '{0}' in selection for '{1}' - Fix all '{0}' in selection for '{1}' - - Project of ID {0} is required to accomplish the task but is not available from the solution Ke splnění úkolu se vyžaduje projekt s ID {0}, který ale není z řešení dostupný. diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/xlf/WorkspaceExtensionsResources.de.xlf b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/xlf/WorkspaceExtensionsResources.de.xlf index d4ba55d45f6db..06c9d633922c4 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/xlf/WorkspaceExtensionsResources.de.xlf +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/xlf/WorkspaceExtensionsResources.de.xlf @@ -42,11 +42,6 @@ Fix all '{0}' in containing type for '{1}' - - Fix all '{0}' in selection for '{1}' - Fix all '{0}' in selection for '{1}' - - Project of ID {0} is required to accomplish the task but is not available from the solution Ein Projekt mit der ID "{0}" ist zum Ausführen der Aufgabe erforderlich, steht aber in der Projektmappe nicht zur Verfügung. diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/xlf/WorkspaceExtensionsResources.es.xlf b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/xlf/WorkspaceExtensionsResources.es.xlf index 8fe36f760c785..f8880e690dc70 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/xlf/WorkspaceExtensionsResources.es.xlf +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/xlf/WorkspaceExtensionsResources.es.xlf @@ -42,11 +42,6 @@ Fix all '{0}' in containing type for '{1}' - - Fix all '{0}' in selection for '{1}' - Fix all '{0}' in selection for '{1}' - - Project of ID {0} is required to accomplish the task but is not available from the solution Se necesita el identificador de proyecto "{0}" para realizar la tarea, pero no está disponibles en la solución diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/xlf/WorkspaceExtensionsResources.fr.xlf b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/xlf/WorkspaceExtensionsResources.fr.xlf index 4c9258ea3fd73..bb1b6d97e701e 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/xlf/WorkspaceExtensionsResources.fr.xlf +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/xlf/WorkspaceExtensionsResources.fr.xlf @@ -42,11 +42,6 @@ Fix all '{0}' in containing type for '{1}' - - Fix all '{0}' in selection for '{1}' - Fix all '{0}' in selection for '{1}' - - Project of ID {0} is required to accomplish the task but is not available from the solution Le projet de l'ID {0} est nécessaire pour accomplir la tâche mais n'est pas disponible dans la solution diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/xlf/WorkspaceExtensionsResources.it.xlf b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/xlf/WorkspaceExtensionsResources.it.xlf index 1bd5e939da138..15a7938ccbfb8 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/xlf/WorkspaceExtensionsResources.it.xlf +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/xlf/WorkspaceExtensionsResources.it.xlf @@ -42,11 +42,6 @@ Fix all '{0}' in containing type for '{1}' - - Fix all '{0}' in selection for '{1}' - Fix all '{0}' in selection for '{1}' - - Project of ID {0} is required to accomplish the task but is not available from the solution Per eseguire l'attività, è necessario il progetto con ID '{0}', che però non è disponibile dalla soluzione diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/xlf/WorkspaceExtensionsResources.ja.xlf b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/xlf/WorkspaceExtensionsResources.ja.xlf index 1bfb70dbd75fb..49eee4a16d480 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/xlf/WorkspaceExtensionsResources.ja.xlf +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/xlf/WorkspaceExtensionsResources.ja.xlf @@ -42,11 +42,6 @@ Fix all '{0}' in containing type for '{1}' - - Fix all '{0}' in selection for '{1}' - Fix all '{0}' in selection for '{1}' - - Project of ID {0} is required to accomplish the task but is not available from the solution タスクの完了には ID {0} のプロジェクトが必要ですが、このソリューションからは利用できません diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/xlf/WorkspaceExtensionsResources.ko.xlf b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/xlf/WorkspaceExtensionsResources.ko.xlf index b71380dab1f21..da6cfd8938d9e 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/xlf/WorkspaceExtensionsResources.ko.xlf +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/xlf/WorkspaceExtensionsResources.ko.xlf @@ -42,11 +42,6 @@ Fix all '{0}' in containing type for '{1}' - - Fix all '{0}' in selection for '{1}' - Fix all '{0}' in selection for '{1}' - - Project of ID {0} is required to accomplish the task but is not available from the solution 작업을 수행하는 데 ID가 {0}인 프로젝트가 필요하지만, 솔루션에서 해당 프로젝트를 사용할 수 없습니다. diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/xlf/WorkspaceExtensionsResources.pl.xlf b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/xlf/WorkspaceExtensionsResources.pl.xlf index 6021803dde9fc..5f2a5c6fb2f2d 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/xlf/WorkspaceExtensionsResources.pl.xlf +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/xlf/WorkspaceExtensionsResources.pl.xlf @@ -42,11 +42,6 @@ Fix all '{0}' in containing type for '{1}' - - Fix all '{0}' in selection for '{1}' - Fix all '{0}' in selection for '{1}' - - Project of ID {0} is required to accomplish the task but is not available from the solution Do wykonania zadania wymagany jest projekt o identyfikatorze {0}, ale nie jest on udostępniany przez rozwiązanie diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/xlf/WorkspaceExtensionsResources.pt-BR.xlf b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/xlf/WorkspaceExtensionsResources.pt-BR.xlf index 04eeacd920169..c5c58c0384dfb 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/xlf/WorkspaceExtensionsResources.pt-BR.xlf +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/xlf/WorkspaceExtensionsResources.pt-BR.xlf @@ -42,11 +42,6 @@ Fix all '{0}' in containing type for '{1}' - - Fix all '{0}' in selection for '{1}' - Fix all '{0}' in selection for '{1}' - - Project of ID {0} is required to accomplish the task but is not available from the solution O projeto com a ID {0} é necessário para realizar a tarefa, mas não está disponível na solução diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/xlf/WorkspaceExtensionsResources.ru.xlf b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/xlf/WorkspaceExtensionsResources.ru.xlf index f329c86e19553..ad61dbffe6086 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/xlf/WorkspaceExtensionsResources.ru.xlf +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/xlf/WorkspaceExtensionsResources.ru.xlf @@ -42,11 +42,6 @@ Fix all '{0}' in containing type for '{1}' - - Fix all '{0}' in selection for '{1}' - Fix all '{0}' in selection for '{1}' - - Project of ID {0} is required to accomplish the task but is not available from the solution Проект с ИД "{0}" необходим для выполнения задачи, но он недоступен из решения. diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/xlf/WorkspaceExtensionsResources.tr.xlf b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/xlf/WorkspaceExtensionsResources.tr.xlf index 037f6d2d30b05..f2eaaa51e28fb 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/xlf/WorkspaceExtensionsResources.tr.xlf +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/xlf/WorkspaceExtensionsResources.tr.xlf @@ -42,11 +42,6 @@ Fix all '{0}' in containing type for '{1}' - - Fix all '{0}' in selection for '{1}' - Fix all '{0}' in selection for '{1}' - - Project of ID {0} is required to accomplish the task but is not available from the solution Görevi gerçekleştirmek için '{0}' kimlikli proje gerekli, ancak bu proje çözümde yok diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/xlf/WorkspaceExtensionsResources.zh-Hans.xlf b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/xlf/WorkspaceExtensionsResources.zh-Hans.xlf index 0a9af7afe8a66..b5bcd682fc1e7 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/xlf/WorkspaceExtensionsResources.zh-Hans.xlf +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/xlf/WorkspaceExtensionsResources.zh-Hans.xlf @@ -42,11 +42,6 @@ Fix all '{0}' in containing type for '{1}' - - Fix all '{0}' in selection for '{1}' - Fix all '{0}' in selection for '{1}' - - Project of ID {0} is required to accomplish the task but is not available from the solution 需要 ID 为 {0} 的项目才能完成任务,但无法从解决方案中使用该项目 diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/xlf/WorkspaceExtensionsResources.zh-Hant.xlf b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/xlf/WorkspaceExtensionsResources.zh-Hant.xlf index f77ee3d11f807..9af1e78ea59e7 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/xlf/WorkspaceExtensionsResources.zh-Hant.xlf +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/xlf/WorkspaceExtensionsResources.zh-Hant.xlf @@ -42,11 +42,6 @@ Fix all '{0}' in containing type for '{1}' - - Fix all '{0}' in selection for '{1}' - Fix all '{0}' in selection for '{1}' - - Project of ID {0} is required to accomplish the task but is not available from the solution 完成工作需要識別碼為 {0} 的專案,但是無法從解決方案取得 From b46362073a9616c18c96219bc27786e951bb852f Mon Sep 17 00:00:00 2001 From: Manish Vasani Date: Mon, 11 Apr 2022 03:07:02 -0700 Subject: [PATCH 08/18] Nullable enable --- .../FixAllCodeRefactoringGetFixesService.cs | 15 ++++++++------- .../FixAllOccurrences/FixSomeCodeAction.cs | 19 ++++++++----------- .../FixAllCodeRefactoringCodeAction.cs | 14 +++++--------- .../IFixAllCodeRefactoringGetFixesService.cs | 4 +--- .../FixAllOccurences/FixAllContext.cs | 8 +------- .../CodeRefactorings/FixAll/FixAllState.cs | 8 ++++---- 6 files changed, 27 insertions(+), 41 deletions(-) diff --git a/src/EditorFeatures/Core.Wpf/Suggestions/FixAll/FixAllCodeRefactoringGetFixesService.cs b/src/EditorFeatures/Core.Wpf/Suggestions/FixAll/FixAllCodeRefactoringGetFixesService.cs index 5d20645e1f3e4..bffad3a9f1e1d 100644 --- a/src/EditorFeatures/Core.Wpf/Suggestions/FixAll/FixAllCodeRefactoringGetFixesService.cs +++ b/src/EditorFeatures/Core.Wpf/Suggestions/FixAll/FixAllCodeRefactoringGetFixesService.cs @@ -2,10 +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. -#nullable disable - using System; -using System.Collections.Generic; using System.Collections.Immutable; using System.Composition; using System.Threading; @@ -32,7 +29,7 @@ public FixAllCodeRefactoringGetFixesService() public IWorkspaceService CreateService(HostWorkspaceServices workspaceServices) => this; - public async Task GetFixAllChangedSolutionAsync(FixAllContext fixAllContext) + public async Task GetFixAllChangedSolutionAsync(FixAllContext fixAllContext) { var codeAction = await GetFixAllCodeActionAsync(fixAllContext).ConfigureAwait(false); if (codeAction == null) @@ -56,7 +53,7 @@ public async Task> GetFixAllOperationsAsync( codeAction, fixAllContext.State, fixAllContext.CancellationToken).ConfigureAwait(false); } - private static async Task GetFixAllCodeActionAsync(FixAllContext fixAllContext) + private static async Task GetFixAllCodeActionAsync(FixAllContext fixAllContext) { using (Logger.LogBlock( FunctionId.CodeFixes_FixAllOccurrencesComputation, @@ -67,7 +64,7 @@ private static async Task GetFixAllCodeActionAsync(FixAllContext fix }), fixAllContext.CancellationToken)) { - CodeAction action = null; + CodeAction? action = null; try { action = await fixAllContext.FixAllProvider.GetFixAsync(fixAllContext).ConfigureAwait(false); @@ -111,6 +108,10 @@ private static async Task> GetFixAllOperatio cancellationToken.ThrowIfCancellationRequested(); var newSolution = await codeAction.GetChangedSolutionInternalAsync(cancellationToken: cancellationToken).ConfigureAwait(false); + if (newSolution == null) + { + return ImmutableArray.Empty; + } newSolution = PreviewChanges( fixAllState.Project.Solution, @@ -130,7 +131,7 @@ private static async Task> GetFixAllOperatio return GetNewFixAllOperations(operations, newSolution, cancellationToken); } - internal static Solution PreviewChanges( + internal static Solution? PreviewChanges( Solution currentSolution, Solution newSolution, string fixAllPreviewChangesTitle, diff --git a/src/Features/Core/Portable/CodeFixes/FixAllOccurrences/FixSomeCodeAction.cs b/src/Features/Core/Portable/CodeFixes/FixAllOccurrences/FixSomeCodeAction.cs index 74ed9511fde3d..2129a33580f09 100644 --- a/src/Features/Core/Portable/CodeFixes/FixAllOccurrences/FixSomeCodeAction.cs +++ b/src/Features/Core/Portable/CodeFixes/FixAllOccurrences/FixSomeCodeAction.cs @@ -2,8 +2,6 @@ // 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.Collections.Generic; using System.Collections.Immutable; using System.Linq; @@ -38,26 +36,24 @@ internal override Task> ComputeOperationsAsy cancellationToken.ThrowIfCancellationRequested(); FixAllLogger.LogState(FixAllState, IsInternalCodeFixProvider(FixAllState.CodeFixProvider)); - var service = FixAllState.Project.Solution.Workspace.Services.GetService(); + var service = FixAllState.Project.Solution.Workspace.Services.GetRequiredService(); var fixAllContext = new FixAllContext(FixAllState, progressTracker, cancellationToken); - if (progressTracker != null) - progressTracker.Description = FixAllContextHelper.GetDefaultFixAllTitle(fixAllContext); + progressTracker.Description = FixAllContextHelper.GetDefaultFixAllTitle(fixAllContext); return service.GetFixAllOperationsAsync(fixAllContext, _showPreviewChangesDialog); } - internal sealed override Task GetChangedSolutionAsync( + internal sealed override Task GetChangedSolutionAsync( IProgressTracker progressTracker, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); FixAllLogger.LogState(FixAllState, IsInternalCodeFixProvider(FixAllState.CodeFixProvider)); - var service = FixAllState.Project.Solution.Workspace.Services.GetService(); + var service = FixAllState.Project.Solution.Workspace.Services.GetRequiredService(); var fixAllContext = new FixAllContext(FixAllState, progressTracker, cancellationToken); - if (progressTracker != null) - progressTracker.Description = FixAllContextHelper.GetDefaultFixAllTitle(fixAllContext); + progressTracker.Description = FixAllContextHelper.GetDefaultFixAllTitle(fixAllContext); return service.GetFixAllChangedSolutionAsync(fixAllContext); } @@ -68,7 +64,8 @@ private static bool IsInternalCodeFixProvider(CodeFixProvider fixer) if (exportAttributes?.Any() == true) { var exportAttribute = (ExportCodeFixProviderAttribute)exportAttributes.First(); - return s_predefinedCodeFixProviderNames.Contains(exportAttribute.Name); + return !string.IsNullOrEmpty(exportAttribute.Name) + && s_predefinedCodeFixProviderNames.Contains(exportAttribute.Name); } return false; @@ -83,7 +80,7 @@ private static HashSet GetPredefinedCodeFixProviderNames() { if (field.IsStatic) { - names.Add((string)field.GetValue(null)); + names.Add((string)field.GetValue(null)!); } } diff --git a/src/Features/Core/Portable/CodeRefactorings/FixAllOccurences/FixAllCodeRefactoringCodeAction.cs b/src/Features/Core/Portable/CodeRefactorings/FixAllOccurences/FixAllCodeRefactoringCodeAction.cs index 7a7d1feb8fbec..aa40ff6ee57eb 100644 --- a/src/Features/Core/Portable/CodeRefactorings/FixAllOccurences/FixAllCodeRefactoringCodeAction.cs +++ b/src/Features/Core/Portable/CodeRefactorings/FixAllOccurences/FixAllCodeRefactoringCodeAction.cs @@ -2,8 +2,6 @@ // 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.Collections.Immutable; @@ -44,25 +42,23 @@ internal override Task> ComputeOperationsAsy { cancellationToken.ThrowIfCancellationRequested(); - var service = FixAllState.Project.Solution.Workspace.Services.GetService(); + var service = FixAllState.Project.Solution.Workspace.Services.GetRequiredService(); var fixAllContext = new FixAllContext(FixAllState, progressTracker, cancellationToken); - if (progressTracker != null) - progressTracker.Description = FixAllContextHelper.GetDefaultFixAllTitle(fixAllContext); + progressTracker.Description = FixAllContextHelper.GetDefaultFixAllTitle(fixAllContext); return service.GetFixAllOperationsAsync(fixAllContext); } - internal sealed override Task GetChangedSolutionAsync( + internal sealed override Task GetChangedSolutionAsync( IProgressTracker progressTracker, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); - var service = FixAllState.Project.Solution.Workspace.Services.GetService(); + var service = FixAllState.Project.Solution.Workspace.Services.GetRequiredService(); var fixAllContext = new FixAllContext(FixAllState, progressTracker, cancellationToken); - if (progressTracker != null) - progressTracker.Description = FixAllContextHelper.GetDefaultFixAllTitle(fixAllContext); + progressTracker.Description = FixAllContextHelper.GetDefaultFixAllTitle(fixAllContext); return service.GetFixAllChangedSolutionAsync(fixAllContext); } diff --git a/src/Features/Core/Portable/CodeRefactorings/FixAllOccurences/IFixAllCodeRefactoringGetFixesService.cs b/src/Features/Core/Portable/CodeRefactorings/FixAllOccurences/IFixAllCodeRefactoringGetFixesService.cs index ae955c311547d..f64feb1c56b28 100644 --- a/src/Features/Core/Portable/CodeRefactorings/FixAllOccurences/IFixAllCodeRefactoringGetFixesService.cs +++ b/src/Features/Core/Portable/CodeRefactorings/FixAllOccurences/IFixAllCodeRefactoringGetFixesService.cs @@ -2,8 +2,6 @@ // 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.Collections.Immutable; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeActions; @@ -22,6 +20,6 @@ internal interface IFixAllCodeRefactoringGetFixesService : IWorkspaceService /// /// Computes the fix all occurrences code fix and returns the changed solution. /// - Task GetFixAllChangedSolutionAsync(FixAllContext fixAllContext); + Task GetFixAllChangedSolutionAsync(FixAllContext fixAllContext); } } diff --git a/src/Workspaces/Core/Portable/CodeRefactorings/FixAllOccurences/FixAllContext.cs b/src/Workspaces/Core/Portable/CodeRefactorings/FixAllOccurences/FixAllContext.cs index b5d168ce6a3e4..e49ecfd709c0b 100644 --- a/src/Workspaces/Core/Portable/CodeRefactorings/FixAllOccurences/FixAllContext.cs +++ b/src/Workspaces/Core/Portable/CodeRefactorings/FixAllOccurences/FixAllContext.cs @@ -2,16 +2,10 @@ // 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.Generic; -using System.Collections.Immutable; -using System.Linq; using System.Threading; -using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.Shared.Utilities; using Microsoft.CodeAnalysis.Text; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CodeRefactorings { @@ -22,7 +16,7 @@ public sealed class FixAllContext { internal FixAllState State { get; } - internal FixAllProvider? FixAllProvider => State.FixAllProvider; + internal FixAllProvider FixAllProvider => State.FixAllProvider; /// /// Document within which fix all occurrences was triggered. diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeRefactorings/FixAll/FixAllState.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeRefactorings/FixAll/FixAllState.cs index 96f763b7a134d..dc9cfb44f0964 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeRefactorings/FixAll/FixAllState.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeRefactorings/FixAll/FixAllState.cs @@ -14,7 +14,7 @@ internal partial class FixAllState { internal readonly int CorrelationId = LogAggregator.GetNextId(); - public FixAllProvider? FixAllProvider { get; } + public FixAllProvider FixAllProvider { get; } public CodeAction CodeAction { get; } public CodeRefactoringProvider CodeRefactoringProvider { get; } public Document? Document { get; } @@ -24,7 +24,7 @@ internal partial class FixAllState public Solution Solution => this.Project.Solution; internal FixAllState( - FixAllProvider? fixAllProvider, + FixAllProvider fixAllProvider, Document document, CodeRefactoringProvider codeRefactoringProvider, FixAllScope fixAllScope, @@ -39,7 +39,7 @@ internal FixAllState( } internal FixAllState( - FixAllProvider? fixAllProvider, + FixAllProvider fixAllProvider, Project project, CodeRefactoringProvider codeRefactoringProvider, FixAllScope fixAllScope, @@ -53,7 +53,7 @@ internal FixAllState( } private FixAllState( - FixAllProvider? fixAllProvider, + FixAllProvider fixAllProvider, Document? document, Project project, CodeRefactoringProvider codeRefactoringProvider, From b41f72950566fcaf133d0988c120fc187a18c96f Mon Sep 17 00:00:00 2001 From: Manish Vasani Date: Mon, 11 Apr 2022 03:47:11 -0700 Subject: [PATCH 09/18] Share code --- .../FixAll/FixAllCodeFixGetFixesService.cs | 129 +------------- .../FixAllCodeRefactoringGetFixesService.cs | 138 +-------------- .../FixAll/FixAllGetFixesServiceHelper.cs | 159 ++++++++++++++++++ 3 files changed, 171 insertions(+), 255 deletions(-) create mode 100644 src/EditorFeatures/Core.Wpf/Suggestions/FixAll/FixAllGetFixesServiceHelper.cs diff --git a/src/EditorFeatures/Core.Wpf/Suggestions/FixAll/FixAllCodeFixGetFixesService.cs b/src/EditorFeatures/Core.Wpf/Suggestions/FixAll/FixAllCodeFixGetFixesService.cs index 617a77c56f961..eb1777758309e 100644 --- a/src/EditorFeatures/Core.Wpf/Suggestions/FixAll/FixAllCodeFixGetFixesService.cs +++ b/src/EditorFeatures/Core.Wpf/Suggestions/FixAll/FixAllCodeFixGetFixesService.cs @@ -5,18 +5,15 @@ #nullable disable using System; -using System.Collections.Generic; using System.Collections.Immutable; using System.Composition; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; -using Microsoft.CodeAnalysis.Editor.Host; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Internal.Log; -using Microsoft.CodeAnalysis.PooledObjects; namespace Microsoft.CodeAnalysis.Editor.Implementation.Suggestions { @@ -53,8 +50,10 @@ public async Task> GetFixAllOperationsAsync( return ImmutableArray.Empty; } - return await GetFixAllOperationsAsync( - codeAction, showPreviewChangesDialog, fixAllContext.State, fixAllContext.CancellationToken).ConfigureAwait(false); + return await FixAllGetFixesServiceHelper.GetFixAllOperationsAsync( + codeAction, fixAllContext.State.Project, fixAllContext.State.CorrelationId, + FunctionId.CodeFixes_FixAllOccurrencesPreviewChanges, + showPreviewChangesDialog, fixAllContext.CancellationToken).ConfigureAwait(false); } private static async Task GetFixAllCodeActionAsync(FixAllContext fixAllContext) @@ -93,46 +92,6 @@ private static async Task GetFixAllCodeActionAsync(FixAllContext fix } } - private static async Task> GetFixAllOperationsAsync( - CodeAction codeAction, bool showPreviewChangesDialog, - FixAllState fixAllState, CancellationToken cancellationToken) - { - // We have computed the fix all occurrences code fix. - // Now fetch the new solution with applied fix and bring up the Preview changes dialog. - - var workspace = fixAllState.Project.Solution.Workspace; - - cancellationToken.ThrowIfCancellationRequested(); - var operations = await codeAction.GetOperationsAsync(cancellationToken).ConfigureAwait(false); - if (operations == null) - { - return ImmutableArray.Empty; - } - - cancellationToken.ThrowIfCancellationRequested(); - var newSolution = await codeAction.GetChangedSolutionInternalAsync(cancellationToken: cancellationToken).ConfigureAwait(false); - - if (showPreviewChangesDialog) - { - newSolution = PreviewChanges( - fixAllState.Project.Solution, - newSolution, - FeaturesResources.Fix_all_occurrences, - codeAction.Title, - fixAllState.Project.Language, - workspace, - fixAllState.CorrelationId, - cancellationToken); - if (newSolution == null) - { - return ImmutableArray.Empty; - } - } - - // Get a code action, with apply changes operation replaced with the newSolution. - return GetNewFixAllOperations(operations, newSolution, cancellationToken); - } - internal static Solution PreviewChanges( Solution currentSolution, Solution newSolution, @@ -142,82 +101,10 @@ internal static Solution PreviewChanges( Workspace workspace, int? correlationId = null, CancellationToken cancellationToken = default) - { - cancellationToken.ThrowIfCancellationRequested(); - using (Logger.LogBlock( + => FixAllGetFixesServiceHelper.PreviewChanges( + currentSolution, newSolution, fixAllPreviewChangesTitle, + fixAllTopLevelHeader, languageOpt, workspace, FunctionId.CodeFixes_FixAllOccurrencesPreviewChanges, - KeyValueLogMessage.Create(LogType.UserAction, m => - { - // only set when correlation id is given - // we might not have this info for suppression - if (correlationId.HasValue) - { - m[FixAllLogger.CorrelationId] = correlationId; - } - }), - cancellationToken)) - { - var glyph = languageOpt == null - ? Glyph.Assembly - : languageOpt == LanguageNames.CSharp - ? Glyph.CSharpProject - : Glyph.BasicProject; -#if COCOA - - var previewService = workspace.Services.GetService(); - - // Until IPreviewDialogService is implemented, just execute all changes without user ability to pick and choose - if (previewService == null) - return newSolution; -#else - - var previewService = workspace.Services.GetRequiredService(); - -#endif - - var changedSolution = previewService.PreviewChanges( - string.Format(EditorFeaturesResources.Preview_Changes_0, fixAllPreviewChangesTitle), - "vs.codefix.fixall", - fixAllTopLevelHeader, - fixAllPreviewChangesTitle, - glyph, - newSolution, - currentSolution); - - if (changedSolution == null) - { - // User clicked cancel. - FixAllLogger.LogPreviewChangesResult(correlationId, applied: false); - return null; - } - - FixAllLogger.LogPreviewChangesResult(correlationId, applied: true, allChangesApplied: changedSolution == newSolution); - return changedSolution; - } - } - - private static ImmutableArray GetNewFixAllOperations(ImmutableArray operations, Solution newSolution, CancellationToken cancellationToken) - { - var result = ArrayBuilder.GetInstance(); - var foundApplyChanges = false; - foreach (var operation in operations) - { - cancellationToken.ThrowIfCancellationRequested(); - - if (!foundApplyChanges) - { - if (operation is ApplyChangesOperation) - { - foundApplyChanges = true; - result.Add(new ApplyChangesOperation(newSolution)); - continue; - } - } - - result.Add(operation); - } - - return result.ToImmutableAndFree(); - } + correlationId, cancellationToken); } } diff --git a/src/EditorFeatures/Core.Wpf/Suggestions/FixAll/FixAllCodeRefactoringGetFixesService.cs b/src/EditorFeatures/Core.Wpf/Suggestions/FixAll/FixAllCodeRefactoringGetFixesService.cs index bffad3a9f1e1d..c1843dd3cac13 100644 --- a/src/EditorFeatures/Core.Wpf/Suggestions/FixAll/FixAllCodeRefactoringGetFixesService.cs +++ b/src/EditorFeatures/Core.Wpf/Suggestions/FixAll/FixAllCodeRefactoringGetFixesService.cs @@ -5,15 +5,12 @@ using System; using System.Collections.Immutable; using System.Composition; -using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeRefactorings; -using Microsoft.CodeAnalysis.Editor.Host; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Internal.Log; -using Microsoft.CodeAnalysis.PooledObjects; namespace Microsoft.CodeAnalysis.Editor.Implementation.Suggestions { @@ -49,8 +46,10 @@ public async Task> GetFixAllOperationsAsync( return ImmutableArray.Empty; } - return await GetFixAllOperationsAsync( - codeAction, fixAllContext.State, fixAllContext.CancellationToken).ConfigureAwait(false); + return await FixAllGetFixesServiceHelper.GetFixAllOperationsAsync( + codeAction, fixAllContext.State.Project, fixAllContext.State.CorrelationId, + FunctionId.Refactoring_FixAllOccurrencesPreviewChanges, + showPreviewChangesDialog: true, fixAllContext.CancellationToken).ConfigureAwait(false); } private static async Task GetFixAllCodeActionAsync(FixAllContext fixAllContext) @@ -88,134 +87,5 @@ public async Task> GetFixAllOperationsAsync( return action; } } - - private static async Task> GetFixAllOperationsAsync( - CodeAction codeAction, - FixAllState fixAllState, - CancellationToken cancellationToken) - { - // We have computed the fix all occurrences code fix. - // Now fetch the new solution with applied fix and bring up the Preview changes dialog. - - var workspace = fixAllState.Project.Solution.Workspace; - - cancellationToken.ThrowIfCancellationRequested(); - var operations = await codeAction.GetOperationsAsync(cancellationToken).ConfigureAwait(false); - if (operations == null) - { - return ImmutableArray.Empty; - } - - cancellationToken.ThrowIfCancellationRequested(); - var newSolution = await codeAction.GetChangedSolutionInternalAsync(cancellationToken: cancellationToken).ConfigureAwait(false); - if (newSolution == null) - { - return ImmutableArray.Empty; - } - - newSolution = PreviewChanges( - fixAllState.Project.Solution, - newSolution, - FeaturesResources.Fix_all_occurrences, - codeAction.Title, - fixAllState.Project.Language, - workspace, - fixAllState.CorrelationId, - cancellationToken); - if (newSolution == null) - { - return ImmutableArray.Empty; - } - - // Get a code action, with apply changes operation replaced with the newSolution. - return GetNewFixAllOperations(operations, newSolution, cancellationToken); - } - - internal static Solution? PreviewChanges( - Solution currentSolution, - Solution newSolution, - string fixAllPreviewChangesTitle, - string fixAllTopLevelHeader, - string languageOpt, - Workspace workspace, - int? correlationId = null, - CancellationToken cancellationToken = default) - { - cancellationToken.ThrowIfCancellationRequested(); - using (Logger.LogBlock( - FunctionId.Refactoring_FixAllOccurrencesPreviewChanges, - KeyValueLogMessage.Create(LogType.UserAction, m => - { - // only set when correlation id is given - // we might not have this info for suppression - if (correlationId.HasValue) - { - m[FixAllLogger.CorrelationId] = correlationId; - } - }), - cancellationToken)) - { - var glyph = languageOpt == null - ? Glyph.Assembly - : languageOpt == LanguageNames.CSharp - ? Glyph.CSharpProject - : Glyph.BasicProject; -#if COCOA - - var previewService = workspace.Services.GetService(); - - // Until IPreviewDialogService is implemented, just execute all changes without user ability to pick and choose - if (previewService == null) - return newSolution; -#else - - var previewService = workspace.Services.GetRequiredService(); - -#endif - - var changedSolution = previewService.PreviewChanges( - string.Format(EditorFeaturesResources.Preview_Changes_0, fixAllPreviewChangesTitle), - "vs.coderefactoring.fixall", - fixAllTopLevelHeader, - fixAllPreviewChangesTitle, - glyph, - newSolution, - currentSolution); - - if (changedSolution == null) - { - // User clicked cancel. - FixAllLogger.LogPreviewChangesResult(correlationId, applied: false); - return null; - } - - FixAllLogger.LogPreviewChangesResult(correlationId, applied: true, allChangesApplied: changedSolution == newSolution); - return changedSolution; - } - } - - private static ImmutableArray GetNewFixAllOperations(ImmutableArray operations, Solution newSolution, CancellationToken cancellationToken) - { - var result = ArrayBuilder.GetInstance(); - var foundApplyChanges = false; - foreach (var operation in operations) - { - cancellationToken.ThrowIfCancellationRequested(); - - if (!foundApplyChanges) - { - if (operation is ApplyChangesOperation) - { - foundApplyChanges = true; - result.Add(new ApplyChangesOperation(newSolution)); - continue; - } - } - - result.Add(operation); - } - - return result.ToImmutableAndFree(); - } } } diff --git a/src/EditorFeatures/Core.Wpf/Suggestions/FixAll/FixAllGetFixesServiceHelper.cs b/src/EditorFeatures/Core.Wpf/Suggestions/FixAll/FixAllGetFixesServiceHelper.cs new file mode 100644 index 0000000000000..b58afcef1f763 --- /dev/null +++ b/src/EditorFeatures/Core.Wpf/Suggestions/FixAll/FixAllGetFixesServiceHelper.cs @@ -0,0 +1,159 @@ +// 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.Collections.Immutable; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.CodeActions; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.Editor.Host; +using Microsoft.CodeAnalysis.Internal.Log; +using Microsoft.CodeAnalysis.PooledObjects; + +namespace Microsoft.CodeAnalysis.Editor.Implementation.Suggestions +{ + /// + /// Helper class for shared code between + /// and . + /// + internal static class FixAllGetFixesServiceHelper + { + public static async Task> GetFixAllOperationsAsync( + CodeAction codeAction, + Project project, + int fixAllCorrelationId, + FunctionId fixAllOccurrencesPreviewChangesFunctionId, + bool showPreviewChangesDialog, + CancellationToken cancellationToken) + { + // We have computed the fix all occurrences code fix. + // Now fetch the new solution with applied fix and bring up the Preview changes dialog. + + var workspace = project.Solution.Workspace; + + cancellationToken.ThrowIfCancellationRequested(); + var operations = await codeAction.GetOperationsAsync(cancellationToken).ConfigureAwait(false); + if (operations == null) + { + return ImmutableArray.Empty; + } + + cancellationToken.ThrowIfCancellationRequested(); + var newSolution = await codeAction.GetChangedSolutionInternalAsync(cancellationToken: cancellationToken).ConfigureAwait(false); + if (newSolution == null) + { + return ImmutableArray.Empty; + } + + if (showPreviewChangesDialog) + { + newSolution = PreviewChanges( + project.Solution, + newSolution, + FeaturesResources.Fix_all_occurrences, + codeAction.Title, + project.Language, + workspace, + fixAllOccurrencesPreviewChangesFunctionId, + fixAllCorrelationId, + cancellationToken); + if (newSolution == null) + { + return ImmutableArray.Empty; + } + } + + // Get a code action, with apply changes operation replaced with the newSolution. + return GetNewFixAllOperations(operations, newSolution, cancellationToken); + } + + internal static Solution? PreviewChanges( + Solution currentSolution, + Solution newSolution, + string fixAllPreviewChangesTitle, + string fixAllTopLevelHeader, + string? language, + Workspace workspace, + FunctionId fixAllOccurrencesPreviewChangesFunctionId, + int? correlationId, + CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + using (Logger.LogBlock( + fixAllOccurrencesPreviewChangesFunctionId, + KeyValueLogMessage.Create(LogType.UserAction, m => + { + // only set when correlation id is given + // we might not have this info for suppression + if (correlationId.HasValue) + { + m[FixAllLogger.CorrelationId] = correlationId; + } + }), + cancellationToken)) + { + var glyph = language == null + ? Glyph.Assembly + : language == LanguageNames.CSharp + ? Glyph.CSharpProject + : Glyph.BasicProject; +#if COCOA + + var previewService = workspace.Services.GetService(); + + // Until IPreviewDialogService is implemented, just execute all changes without user ability to pick and choose + if (previewService == null) + return newSolution; +#else + + var previewService = workspace.Services.GetRequiredService(); + +#endif + + var changedSolution = previewService.PreviewChanges( + string.Format(EditorFeaturesResources.Preview_Changes_0, fixAllPreviewChangesTitle), + "vs.codefix.fixall", + fixAllTopLevelHeader, + fixAllPreviewChangesTitle, + glyph, + newSolution, + currentSolution); + + if (changedSolution == null) + { + // User clicked cancel. + FixAllLogger.LogPreviewChangesResult(correlationId, applied: false); + return null; + } + + FixAllLogger.LogPreviewChangesResult(correlationId, applied: true, allChangesApplied: changedSolution == newSolution); + return changedSolution; + } + } + + private static ImmutableArray GetNewFixAllOperations(ImmutableArray operations, Solution newSolution, CancellationToken cancellationToken) + { + var result = ArrayBuilder.GetInstance(); + var foundApplyChanges = false; + foreach (var operation in operations) + { + cancellationToken.ThrowIfCancellationRequested(); + + if (!foundApplyChanges) + { + if (operation is ApplyChangesOperation) + { + foundApplyChanges = true; + result.Add(new ApplyChangesOperation(newSolution)); + continue; + } + } + + result.Add(operation); + } + + return result.ToImmutableAndFree(); + } + } +} From 7ef03b2506dd38326fe315499c5a8e7dd46097ba Mon Sep 17 00:00:00 2001 From: Manish Vasani Date: Mon, 11 Apr 2022 20:58:18 -0700 Subject: [PATCH 10/18] Add code refactoring specific API to IFixAllSpanMappingService and update namespace --- .../FixAllCodeRefactoringSuggestedAction.cs | 9 +--- .../Diagnostics/AbstractUserDiagnosticTest.cs | 1 + ...uggestedActionsSource.CodeFixesComputer.cs | 1 + ...dActionsSource.CodeRefactoringsComputer.cs | 17 ++++++++ .../UnifiedSuggestedActionsSource.cs | 4 -- .../OmniSharpCodeFixContextFactory.cs | 3 +- .../FixAllOccurrences/FixAllContextHelper.cs | 1 + .../CSharpFixAllSpanMappingService.cs | 11 ++--- .../AbstractFixAllSpanMappingService.cs | 42 ++++++++++++------- .../IFixAllSpanMappingService.cs | 25 +++++++---- .../VisualBasicFixAllSpanMappingService.vb | 6 +-- 11 files changed, 75 insertions(+), 45 deletions(-) diff --git a/src/EditorFeatures/Core.Wpf/Suggestions/SuggestedActions/FixAllCodeRefactoringSuggestedAction.cs b/src/EditorFeatures/Core.Wpf/Suggestions/SuggestedActions/FixAllCodeRefactoringSuggestedAction.cs index 5dbae6350e39d..62d02ffb181bc 100644 --- a/src/EditorFeatures/Core.Wpf/Suggestions/SuggestedActions/FixAllCodeRefactoringSuggestedAction.cs +++ b/src/EditorFeatures/Core.Wpf/Suggestions/SuggestedActions/FixAllCodeRefactoringSuggestedAction.cs @@ -2,14 +2,11 @@ // 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.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeRefactorings; -using Microsoft.CodeAnalysis.Editor.Shared.Extensions; using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.CodeAnalysis.Internal.Log; using Microsoft.CodeAnalysis.Shared.Utilities; @@ -25,8 +22,7 @@ namespace Microsoft.CodeAnalysis.Editor.Implementation.Suggestions internal sealed class FixAllCodeRefactoringSuggestedAction : SuggestedAction, IFixAllCodeRefactoringSuggestedAction { /// - /// The original code-action that we are a fix-all for. i.e. _originalCodeAction - /// would be something like "use 'var' instead of 'int'", this suggestion action + /// The original code-action that we are a fix-all for. This suggestion action /// and our is the actual action that /// will perform the fix in the appropriate document/project/solution scope. /// @@ -51,8 +47,7 @@ internal FixAllCodeRefactoringSuggestedAction( public override bool TryGetTelemetryId(out Guid telemetryId) { // We get the telemetry id for the original code action we are fixing, - // not the special 'FixAllCodeAction'. that is the .CodeAction this - // SuggestedAction is pointing at. + // not the special 'FixAllCodeAction'. telemetryId = OriginalCodeAction.GetTelemetryId(FixAllState.FixAllScope); return true; } diff --git a/src/EditorFeatures/DiagnosticsTestUtilities/Diagnostics/AbstractUserDiagnosticTest.cs b/src/EditorFeatures/DiagnosticsTestUtilities/Diagnostics/AbstractUserDiagnosticTest.cs index 3ac1980cbdccf..64e35dc16b639 100644 --- a/src/EditorFeatures/DiagnosticsTestUtilities/Diagnostics/AbstractUserDiagnosticTest.cs +++ b/src/EditorFeatures/DiagnosticsTestUtilities/Diagnostics/AbstractUserDiagnosticTest.cs @@ -26,6 +26,7 @@ using Microsoft.CodeAnalysis.Remote.Testing; using Xunit.Abstractions; using Microsoft.CodeAnalysis.Shared.Extensions; +using Microsoft.CodeAnalysis.FixAll; namespace Microsoft.CodeAnalysis.Editor.UnitTests.Diagnostics { diff --git a/src/Features/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActionsSource.CodeFixesComputer.cs b/src/Features/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActionsSource.CodeFixesComputer.cs index 0a95324ffab17..8d9eee07ff5f2 100644 --- a/src/Features/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActionsSource.CodeFixesComputer.cs +++ b/src/Features/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActionsSource.CodeFixesComputer.cs @@ -11,6 +11,7 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.FixAll; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; diff --git a/src/Features/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActionsSource.CodeRefactoringsComputer.cs b/src/Features/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActionsSource.CodeRefactoringsComputer.cs index 51fa8c1b34735..d322f067dc728 100644 --- a/src/Features/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActionsSource.CodeRefactoringsComputer.cs +++ b/src/Features/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActionsSource.CodeRefactoringsComputer.cs @@ -11,6 +11,7 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeRefactorings; +using Microsoft.CodeAnalysis.FixAll; using Microsoft.CodeAnalysis.LanguageServices; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Extensions; @@ -197,6 +198,22 @@ async Task GetUnifiedSuggestedActionSetAsync(CodeAction using var fixAllSuggestedActionsDisposer = ArrayBuilder.GetInstance(out var fixAllSuggestedActions); foreach (var scope in fixAllProviderInfo.SupportedScopes) { + if (scope is FixAllScope.ContainingMember or FixAllScope.ContainingType) + { + // Skip showing ContainingMember and ContainingType FixAll scopes if the language + // does not implement 'IFixAllSpanMappingService' langauge service or + // we have no mapped FixAll spans to fix. + + var spanMappingService = document.GetLanguageService(); + if (spanMappingService is null) + continue; + + var documentsAndSpans = await spanMappingService.GetFixAllSpansAsync( + document, selection, scope, cancellationToken).ConfigureAwait(false); + if (documentsAndSpans.IsEmpty) + continue; + } + var fixAllSpan = await GetFixAllSpanForScopeAsync(scope).ConfigureAwait(false); var fixAllState = new FixAllState(fixAllProviderInfo.FixAllProvider, document, provider, scope, fixAllSpan, action); var fixAllSuggestedAction = new UnifiedFixAllCodeRefactoringSuggestedAction( diff --git a/src/Features/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActionsSource.cs b/src/Features/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActionsSource.cs index cf5bc85083792..76b4d210f5d67 100644 --- a/src/Features/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActionsSource.cs +++ b/src/Features/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActionsSource.cs @@ -5,7 +5,6 @@ using System; using System.Collections.Generic; using System.Collections.Immutable; -using System.Diagnostics; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -13,11 +12,8 @@ using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.CodeRefactorings; using Microsoft.CodeAnalysis.PooledObjects; -using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; -using static Microsoft.CodeAnalysis.CodeActions.CodeAction; -using CodeFixGroupKey = System.Tuple; namespace Microsoft.CodeAnalysis.UnifiedSuggestions { diff --git a/src/Tools/ExternalAccess/OmniSharp/CodeActions/OmniSharpCodeFixContextFactory.cs b/src/Tools/ExternalAccess/OmniSharp/CodeActions/OmniSharpCodeFixContextFactory.cs index 091695050adb7..90708df40c129 100644 --- a/src/Tools/ExternalAccess/OmniSharp/CodeActions/OmniSharpCodeFixContextFactory.cs +++ b/src/Tools/ExternalAccess/OmniSharp/CodeActions/OmniSharpCodeFixContextFactory.cs @@ -8,7 +8,6 @@ using System.Threading; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; -using Microsoft.CodeAnalysis.CodeRefactorings; using Microsoft.CodeAnalysis.Shared.Utilities; using Microsoft.CodeAnalysis.Text; @@ -25,7 +24,7 @@ public static CodeFixContext CreateCodeFixContext( CancellationToken cancellationToken) => new(document, span, diagnostics, registerCodeFix, options.GetCodeActionOptions(), cancellationToken); - public static CodeRefactoringContext CreateCodeRefactoringContext( + public static CodeAnalysis.CodeRefactorings.CodeRefactoringContext CreateCodeRefactoringContext( Document document, TextSpan span, Action registerRefactoring, diff --git a/src/Workspaces/Core/Portable/CodeFixes/FixAllOccurrences/FixAllContextHelper.cs b/src/Workspaces/Core/Portable/CodeFixes/FixAllOccurrences/FixAllContextHelper.cs index 19fcc511a76fa..cfdd9e246d176 100644 --- a/src/Workspaces/Core/Portable/CodeFixes/FixAllOccurrences/FixAllContextHelper.cs +++ b/src/Workspaces/Core/Portable/CodeFixes/FixAllOccurrences/FixAllContextHelper.cs @@ -8,6 +8,7 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; +using Microsoft.CodeAnalysis.FixAll; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/LanguageServices/CSharpFixAllSpanMappingService.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/LanguageServices/CSharpFixAllSpanMappingService.cs index 6f408772979c7..90667eb34b95e 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/LanguageServices/CSharpFixAllSpanMappingService.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/LanguageServices/CSharpFixAllSpanMappingService.cs @@ -8,14 +8,13 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.FixAll; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; -using Roslyn.Utilities; -namespace Microsoft.CodeAnalysis.CSharp.CodeFixes +namespace Microsoft.CodeAnalysis.CSharp.FixAll { [ExportLanguageService(typeof(IFixAllSpanMappingService), LanguageNames.CSharp), Shared] internal sealed class CSharpFixAllSpanMappingService : AbstractFixAllSpanMappingService @@ -27,12 +26,10 @@ public CSharpFixAllSpanMappingService() } protected override async Task>> GetFixAllSpansIfWithinGlobalStatementAsync( - Document document, TextSpan diagnosticSpan, FixAllScope fixAllScope, CancellationToken cancellationToken) + Document document, TextSpan span, CancellationToken cancellationToken) { - Contract.ThrowIfFalse(fixAllScope is FixAllScope.ContainingMember or FixAllScope.ContainingType); - var root = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); - var node = root.FindNode(diagnosticSpan); + var node = root.FindNode(span); if (node.GetAncestorOrThis() is null) return ImmutableDictionary>.Empty; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/LanguageServices/FixAllSpanMappingService/AbstractFixAllSpanMappingService.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/LanguageServices/FixAllSpanMappingService/AbstractFixAllSpanMappingService.cs index 994da54ee851b..b084dabf87aad 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/LanguageServices/FixAllSpanMappingService/AbstractFixAllSpanMappingService.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/LanguageServices/FixAllSpanMappingService/AbstractFixAllSpanMappingService.cs @@ -11,23 +11,39 @@ using Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; -namespace Microsoft.CodeAnalysis.CodeFixes +namespace Microsoft.CodeAnalysis.FixAll { internal abstract class AbstractFixAllSpanMappingService : IFixAllSpanMappingService { protected abstract Task>> GetFixAllSpansIfWithinGlobalStatementAsync( - Document document, TextSpan diagnosticSpan, FixAllScope fixAllScope, CancellationToken cancellationToken); + Document document, TextSpan span, CancellationToken cancellationToken); - public async Task>> GetFixAllSpansAsync( - Document document, TextSpan diagnosticSpan, FixAllScope fixAllScope, CancellationToken cancellationToken) + public Task>> GetFixAllSpansAsync( + Document document, TextSpan diagnosticSpan, CodeFixes.FixAllScope fixAllScope, CancellationToken cancellationToken) { - Contract.ThrowIfFalse(fixAllScope is FixAllScope.ContainingMember or FixAllScope.ContainingType); + Contract.ThrowIfFalse(fixAllScope is CodeFixes.FixAllScope.ContainingMember or CodeFixes.FixAllScope.ContainingType); - var decl = await GetContainingMemberOrTypeDeclarationAsync(document, fixAllScope, diagnosticSpan, cancellationToken).ConfigureAwait(false); + var fixAllInContainingMember = fixAllScope == CodeFixes.FixAllScope.ContainingMember; + return GetFixAllSpansAsync(document, diagnosticSpan, fixAllInContainingMember, cancellationToken); + } + + public Task>> GetFixAllSpansAsync( + Document document, TextSpan diagnosticSpan, CodeRefactorings.FixAllScope fixAllScope, CancellationToken cancellationToken) + { + Contract.ThrowIfFalse(fixAllScope is CodeRefactorings.FixAllScope.ContainingMember or CodeRefactorings.FixAllScope.ContainingType); + + var fixAllInContainingMember = fixAllScope == CodeRefactorings.FixAllScope.ContainingMember; + return GetFixAllSpansAsync(document, diagnosticSpan, fixAllInContainingMember, cancellationToken); + } + + private async Task>> GetFixAllSpansAsync( + Document document, TextSpan span, bool fixAllInContainingMember, CancellationToken cancellationToken) + { + var decl = await GetContainingMemberOrTypeDeclarationAsync(document, fixAllInContainingMember, span, cancellationToken).ConfigureAwait(false); if (decl == null) - return await GetFixAllSpansIfWithinGlobalStatementAsync(document, diagnosticSpan, fixAllScope, cancellationToken).ConfigureAwait(false); + return await GetFixAllSpansIfWithinGlobalStatementAsync(document, span, cancellationToken).ConfigureAwait(false); - if (fixAllScope == FixAllScope.ContainingMember) + if (fixAllInContainingMember) { return ImmutableDictionary.CreateRange(SpecializedCollections.SingletonEnumerable( KeyValuePairUtil.Create(document, ImmutableArray.Create(decl.FullSpan)))); @@ -61,29 +77,27 @@ public async Task>> GetFi private static async Task GetContainingMemberOrTypeDeclarationAsync( Document document, - FixAllScope fixAllScope, + bool fixAllInContainingMember, TextSpan span, CancellationToken cancellationToken) { - Contract.ThrowIfFalse(fixAllScope is FixAllScope.ContainingMember or FixAllScope.ContainingType); - var root = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); var syntaxFacts = document.GetRequiredLanguageService(); - var startContainer = fixAllScope == FixAllScope.ContainingMember + var startContainer = fixAllInContainingMember ? syntaxFacts.GetContainingMemberDeclaration(root, span.Start) : syntaxFacts.GetContainingTypeDeclaration(root, span.Start); if (startContainer == null) return null; - if (fixAllScope == FixAllScope.ContainingMember && !syntaxFacts.IsMethodLevelMember(startContainer)) + if (fixAllInContainingMember && !syntaxFacts.IsMethodLevelMember(startContainer)) return null; if (span.IsEmpty) return startContainer; - var endContainer = fixAllScope == FixAllScope.ContainingMember + var endContainer = fixAllInContainingMember ? syntaxFacts.GetContainingMemberDeclaration(root, span.End) : syntaxFacts.GetContainingTypeDeclaration(root, span.End); diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/LanguageServices/FixAllSpanMappingService/IFixAllSpanMappingService.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/LanguageServices/FixAllSpanMappingService/IFixAllSpanMappingService.cs index 8d0d8cd698218..bf5ef4c145fe0 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/LanguageServices/FixAllSpanMappingService/IFixAllSpanMappingService.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/LanguageServices/FixAllSpanMappingService/IFixAllSpanMappingService.cs @@ -8,13 +8,13 @@ using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Text; -namespace Microsoft.CodeAnalysis.CodeFixes +namespace Microsoft.CodeAnalysis.FixAll { /// - /// Language service for mapping spans for specific s for fix all occurences code fix. - /// Every language that wants to support span based FixAll scopes, such as , - /// , should implement this language service. Non-span based FixAll scopes, - /// such as , and + /// Language service for mapping spans for specific FixAllScopes for fix all occurences code fix. + /// Every language that wants to support span based FixAll scopes, such as FixAllScope.ContainingMember, + /// FixAllScope.ContainingType, should implement this language service. Non-span based FixAll scopes, + /// such as FixAllScope.Document, FixAllScope.Project and FixAllScope.Solution /// do not require such a span mapping, and this service will never be called for these scopes. This language service /// does not need to be implemented by languages that only intend to support these non-span based FixAll scopes. /// @@ -23,10 +23,19 @@ internal interface IFixAllSpanMappingService : ILanguageService /// /// For the given and in the given , /// returns the documents and fix all spans within each document that need to be fixed. - /// Note that this API is only invoked for span based FixAll scopes, i.e. - /// and . + /// Note that this API is only invoked for span based FixAll scopes, i.e. + /// and . /// Task>> GetFixAllSpansAsync( - Document document, TextSpan diagnosticSpan, FixAllScope fixAllScope, CancellationToken cancellationToken); + Document document, TextSpan diagnosticSpan, CodeFixes.FixAllScope fixAllScope, CancellationToken cancellationToken); + + /// + /// For the given and in the given , + /// returns the documents and fix all spans within each document that need to be fixed. + /// Note that this API is only invoked for span based FixAll scopes, i.e. + /// and . + /// + Task>> GetFixAllSpansAsync( + Document document, TextSpan selectionSpan, CodeRefactorings.FixAllScope fixAllScope, CancellationToken cancellationToken); } } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/VisualBasic/LanguageServices/VisualBasicFixAllSpanMappingService.vb b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/VisualBasic/LanguageServices/VisualBasicFixAllSpanMappingService.vb index 800082d2a22b7..e0e16f755b41c 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/VisualBasic/LanguageServices/VisualBasicFixAllSpanMappingService.vb +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/VisualBasic/LanguageServices/VisualBasicFixAllSpanMappingService.vb @@ -5,11 +5,11 @@ Imports System.Collections.Immutable Imports System.Composition Imports System.Threading -Imports Microsoft.CodeAnalysis.CodeFixes +Imports Microsoft.CodeAnalysis.FixAll Imports Microsoft.CodeAnalysis.Host.Mef Imports Microsoft.CodeAnalysis.Text -Namespace Microsoft.CodeAnalysis.VisualBasic.CodeFixes +Namespace Microsoft.CodeAnalysis.VisualBasic.FixAll Friend Class VisualBasicFixAllSpanMappingService Inherits AbstractFixAllSpanMappingService @@ -19,7 +19,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeFixes Public Sub New() End Sub - Protected Overrides Function GetFixAllSpansIfWithinGlobalStatementAsync(document As Document, diagnosticSpan As TextSpan, fixAllScope As FixAllScope, cancellationToken As CancellationToken) As Task(Of ImmutableDictionary(Of Document, ImmutableArray(Of TextSpan))) + Protected Overrides Function GetFixAllSpansIfWithinGlobalStatementAsync(document As Document, diagnosticSpan As TextSpan, cancellationToken As CancellationToken) As Task(Of ImmutableDictionary(Of Document, ImmutableArray(Of TextSpan))) ' VB does not support global statements Return Task.FromResult(ImmutableDictionary(Of Document, ImmutableArray(Of TextSpan)).Empty) End Function From 80dc735576021cdf62c9141db22858b61a75b31e Mon Sep 17 00:00:00 2001 From: Manish Vasani Date: Tue, 12 Apr 2022 02:25:39 -0700 Subject: [PATCH 11/18] Cleanup to make the APIs consistent with FixAll APIs for code fixes --- ...nvertConditionalCodeRefactoringProvider.cs | 12 +- ...iedFixAllCodeRefactoringSuggestedAction.cs | 4 +- ...dActionsSource.CodeRefactoringsComputer.cs | 45 +------- .../FixAllOccurrences/FixAllProvider.cs | 5 +- .../DocumentBasedFixAllProvider.cs | 52 +++------ .../FixAllOccurences/FixAllContext.cs | 33 ++++-- .../FixAllOccurences/FixAllContextHelper.cs | 3 +- .../FixAllOccurences/FixAllProvider.cs | 59 ++++++---- .../Core/Portable/PublicAPI.Unshipped.txt | 12 +- .../CodeRefactorings/FixAll/FixAllState.cs | 108 +++++++++++++----- ...yntaxEditorBasedCodeRefactoringProvider.cs | 34 +++--- 11 files changed, 193 insertions(+), 174 deletions(-) diff --git a/src/Features/Core/Portable/InvertConditional/AbstractInvertConditionalCodeRefactoringProvider.cs b/src/Features/Core/Portable/InvertConditional/AbstractInvertConditionalCodeRefactoringProvider.cs index 5881e39e2c84a..3d5e261a81b85 100644 --- a/src/Features/Core/Portable/InvertConditional/AbstractInvertConditionalCodeRefactoringProvider.cs +++ b/src/Features/Core/Portable/InvertConditional/AbstractInvertConditionalCodeRefactoringProvider.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System; +using System.Collections.Immutable; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -20,6 +21,8 @@ internal abstract class AbstractInvertConditionalCodeRefactoringProvider SupportedFixAllScopes => AllFixAllScopes; + public override async Task ComputeRefactoringsAsync(CodeRefactoringContext context) { var (document, span, cancellationToken) = context; @@ -39,12 +42,13 @@ public override async Task ComputeRefactoringsAsync(CodeRefactoringContext conte Document document, TextSpan span, CancellationToken cancellationToken) => await document.TryGetRelevantNodeAsync(span, cancellationToken).ConfigureAwait(false); - protected override async Task FixAllAsync(Document document, TextSpan fixAllSpan, CodeAction? originalCodeAction, SyntaxEditor editor, CancellationToken cancellationToken) + protected override async Task FixAllAsync(Document document, ImmutableArray fixAllSpans, SyntaxEditor editor, CancellationToken cancellationToken) { var originalRoot = editor.OriginalRoot; - // Get all conditional nodes in the given fixAllSpan. - var conditionals = originalRoot.DescendantNodes(node => fixAllSpan.IntersectsWith(node.Span)).OfType(); + // Get all conditional nodes in the given fixAllSpans. + var conditionals = originalRoot.DescendantNodes().OfType() + .Where(node => fixAllSpans.Any(fixAllSpan => fixAllSpan.IntersectsWith(node.Span))); // We're going to be continually editing this tree. Track all the nodes we // care about so we can find them across each edit. @@ -53,7 +57,7 @@ protected override async Task FixAllAsync(Document document, TextSpan fixAllSpan foreach (var originalConditional in conditionals.Reverse()) { // Only process conditionals fully within fixAllSpan - if (!fixAllSpan.Contains(originalConditional.Span)) + if (!fixAllSpans.Any(fixAllSpan => fixAllSpan.Contains(originalConditional.Span))) continue; var currentRoot = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); diff --git a/src/Features/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActions/UnifiedFixAllCodeRefactoringSuggestedAction.cs b/src/Features/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActions/UnifiedFixAllCodeRefactoringSuggestedAction.cs index f05c4442d0b8b..0ec10a107bd92 100644 --- a/src/Features/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActions/UnifiedFixAllCodeRefactoringSuggestedAction.cs +++ b/src/Features/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActions/UnifiedFixAllCodeRefactoringSuggestedAction.cs @@ -14,13 +14,13 @@ namespace Microsoft.CodeAnalysis.UnifiedSuggestions /// internal class UnifiedFixAllCodeRefactoringSuggestedAction : UnifiedSuggestedAction, IFixAllCodeRefactoringSuggestedAction { - public FixAllState? FixAllState { get; } + public FixAllState FixAllState { get; } public UnifiedFixAllCodeRefactoringSuggestedAction( Workspace workspace, CodeAction codeAction, CodeActionPriority codeActionPriority, - FixAllState? fixAllState) + FixAllState fixAllState) : base(workspace, codeAction, codeActionPriority) { FixAllState = fixAllState; diff --git a/src/Features/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActionsSource.CodeRefactoringsComputer.cs b/src/Features/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActionsSource.CodeRefactoringsComputer.cs index d322f067dc728..33013cc75b2d0 100644 --- a/src/Features/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActionsSource.CodeRefactoringsComputer.cs +++ b/src/Features/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActionsSource.CodeRefactoringsComputer.cs @@ -198,24 +198,18 @@ async Task GetUnifiedSuggestedActionSetAsync(CodeAction using var fixAllSuggestedActionsDisposer = ArrayBuilder.GetInstance(out var fixAllSuggestedActions); foreach (var scope in fixAllProviderInfo.SupportedScopes) { + var fixAllState = new FixAllState(fixAllProviderInfo.FixAllProvider, document, selection, provider, scope, action); + if (scope is FixAllScope.ContainingMember or FixAllScope.ContainingType) { // Skip showing ContainingMember and ContainingType FixAll scopes if the language // does not implement 'IFixAllSpanMappingService' langauge service or // we have no mapped FixAll spans to fix. - - var spanMappingService = document.GetLanguageService(); - if (spanMappingService is null) - continue; - - var documentsAndSpans = await spanMappingService.GetFixAllSpansAsync( - document, selection, scope, cancellationToken).ConfigureAwait(false); + var documentsAndSpans = await fixAllState.GetFixAllSpansAsync(cancellationToken).ConfigureAwait(false); if (documentsAndSpans.IsEmpty) continue; } - var fixAllSpan = await GetFixAllSpanForScopeAsync(scope).ConfigureAwait(false); - var fixAllState = new FixAllState(fixAllProviderInfo.FixAllProvider, document, provider, scope, fixAllSpan, action); var fixAllSuggestedAction = new UnifiedFixAllCodeRefactoringSuggestedAction( workspace, action, action.Priority, fixAllState); @@ -228,39 +222,6 @@ async Task GetUnifiedSuggestedActionSetAsync(CodeAction title: CodeFixesResources.Fix_all_occurrences_in, priority: UnifiedSuggestedActionSetPriority.Lowest, applicableToSpan: null); - - // Local functions - async Task GetFixAllSpanForScopeAsync(FixAllScope fixAllScope) - { - return fixAllScope switch - { - FixAllScope.ContainingMember or FixAllScope.ContainingType - => await GetSpanForContainingMemberOrTypeAsync(fixAllScope).ConfigureAwait(false), - _ => null, - }; - } - - async Task GetSpanForContainingMemberOrTypeAsync(FixAllScope fixAllScope) - { - Contract.ThrowIfFalse(fixAllScope is FixAllScope.ContainingMember or FixAllScope.ContainingType); - - var root = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); - var syntaxFacts = document.GetRequiredLanguageService(); - - var startContainer = fixAllScope == FixAllScope.ContainingMember - ? syntaxFacts.GetContainingMemberDeclaration(root, selection.Start) - : syntaxFacts.GetContainingTypeDeclaration(root, selection.Start); - if (selection.IsEmpty || startContainer == null) - return startContainer?.FullSpan; - - var endContainer = fixAllScope == FixAllScope.ContainingMember - ? syntaxFacts.GetContainingMemberDeclaration(root, selection.End) - : syntaxFacts.GetContainingTypeDeclaration(root, selection.End); - if (startContainer == endContainer) - return startContainer.FullSpan; - - return null; - } } } } diff --git a/src/Workspaces/Core/Portable/CodeFixes/FixAllOccurrences/FixAllProvider.cs b/src/Workspaces/Core/Portable/CodeFixes/FixAllOccurrences/FixAllProvider.cs index cf7d00728df8f..2972bd3e12a77 100644 --- a/src/Workspaces/Core/Portable/CodeFixes/FixAllOccurrences/FixAllProvider.cs +++ b/src/Workspaces/Core/Portable/CodeFixes/FixAllOccurrences/FixAllProvider.cs @@ -74,12 +74,9 @@ public static FixAllProvider Create(Func public static FixAllProvider Create( - Func, Task> fixAllAsync, + Func, Task> fixAllAsync!!, ImmutableArray supportedFixAllScopes) { - if (fixAllAsync == null) - throw new ArgumentNullException(nameof(fixAllAsync)); - if (supportedFixAllScopes.IsDefault) throw new ArgumentNullException(nameof(supportedFixAllScopes)); diff --git a/src/Workspaces/Core/Portable/CodeRefactorings/FixAllOccurences/DocumentBasedFixAllProvider.cs b/src/Workspaces/Core/Portable/CodeRefactorings/FixAllOccurences/DocumentBasedFixAllProvider.cs index 315d23025e959..58da72b6e8c1e 100644 --- a/src/Workspaces/Core/Portable/CodeRefactorings/FixAllOccurences/DocumentBasedFixAllProvider.cs +++ b/src/Workspaces/Core/Portable/CodeRefactorings/FixAllOccurences/DocumentBasedFixAllProvider.cs @@ -26,12 +26,20 @@ namespace Microsoft.CodeAnalysis.CodeRefactorings /// /// This type provides suitable logic for fixing large solutions in an efficient manner. Projects are serially /// processed, with all the documents in the project being processed in parallel. - /// is invoked for each document for implementors to process. + /// is invoked for each document for implementors to process. /// public abstract class DocumentBasedFixAllProvider : FixAllProvider { + private readonly ImmutableArray _supportedFixAllScopes; + protected DocumentBasedFixAllProvider() + : this(DefaultSupportedFixAllScopes) + { + } + + protected DocumentBasedFixAllProvider(ImmutableArray supportedFixAllScopes) { + _supportedFixAllScopes = supportedFixAllScopes; } /// @@ -42,42 +50,23 @@ protected virtual string GetFixAllTitle(FixAllContext fixAllContext) => FixAllContextHelper.GetDefaultFixAllTitle(fixAllContext); /// - /// Apply fix all operation for the in the + /// Apply fix all operation for the code refactoring in the /// for the given . The document returned will only be examined for its content /// (e.g. it's or . No other aspects of document (like it's properties), /// or changes to the or it points at will be considered. /// /// The context for the Fix All operation. + /// The document to fix. + /// The spans to fix in the document. /// /// The new representing the content fixed document. /// -or- /// , if no changes were made to the document. /// - protected abstract Task FixAllAsync(FixAllContext fixAllContext); - - /// - /// Returns a bool indicating if the provider supports FixAll in containing member, - /// i.e. - /// - protected abstract bool SupportsFixAllForContainingMember { get; } - - /// - /// Returns a bool indicating if the provider supports FixAll in containing type declaration, - /// i.e. - /// - protected abstract bool SupportsFixAllForContainingType { get; } + protected abstract Task FixAllAsync(FixAllContext fixAllContext, Document document, ImmutableArray fixAllSpans); public sealed override IEnumerable GetSupportedFixAllScopes() - { - foreach (var defaultScope in base.GetSupportedFixAllScopes()) - yield return defaultScope; - - if (SupportsFixAllForContainingMember) - yield return FixAllScope.ContainingMember; - - if (SupportsFixAllForContainingType) - yield return FixAllScope.ContainingType; - } + => _supportedFixAllScopes; public sealed override Task GetFixAsync(FixAllContext fixAllContext) => GetFixAsync(FixAllContextHelper.GetDefaultFixAllTitle(fixAllContext), fixAllContext, FixAllContextsAsync); @@ -103,12 +92,8 @@ FixAllScope.Document or FixAllScope.ContainingMember or FixAllScope.ContainingTy if (solution == null) return null; -#pragma warning disable RS0005 // Do not use generic 'CodeAction.Create' to create 'CodeAction' - return CodeAction.Create( title, c => Task.FromResult(solution)); - -#pragma warning restore RS0005 // Do not use generic 'CodeAction.Create' to create 'CodeAction' } private static Task GetDocumentFixesAsync(FixAllContext fixAllContext, FixAllContexts fixAllContextsAsync) @@ -195,16 +180,13 @@ private async Task FixSingleContextAsync(Solution currentSolution, Fix var docIdToNewRootOrText = new Dictionary(); // Process all documents in parallel to get the change for each doc. - var documentsToFix = fixAllContext.Scope == FixAllScope.Project - ? fixAllContext.Project.Documents - : SpecializedCollections.SingletonEnumerable(fixAllContext.Document); + var documentsAndSpansToFix = await fixAllContext.GetFixAllSpansAsync(cancellationToken).ConfigureAwait(false); - foreach (var document in documentsToFix) + foreach (var (document, spans) in documentsAndSpansToFix) { tasks.Add(Task.Run(async () => { - var newFixAllContext = fixAllContext.WithDocument(document); - var newDocument = await this.FixAllAsync(fixAllContext).ConfigureAwait(false); + var newDocument = await this.FixAllAsync(fixAllContext, document, spans).ConfigureAwait(false); if (newDocument == null || newDocument == document) return default; diff --git a/src/Workspaces/Core/Portable/CodeRefactorings/FixAllOccurences/FixAllContext.cs b/src/Workspaces/Core/Portable/CodeRefactorings/FixAllOccurences/FixAllContext.cs index e49ecfd709c0b..7e449ee1aa5ad 100644 --- a/src/Workspaces/Core/Portable/CodeRefactorings/FixAllOccurences/FixAllContext.cs +++ b/src/Workspaces/Core/Portable/CodeRefactorings/FixAllOccurences/FixAllContext.cs @@ -2,10 +2,19 @@ // 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.Collections; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Linq; using System.Threading; +using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeActions; +using Microsoft.CodeAnalysis.FixAll; +using Microsoft.CodeAnalysis.PooledObjects; +using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Shared.Utilities; using Microsoft.CodeAnalysis.Text; +using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CodeRefactorings { @@ -23,6 +32,11 @@ public sealed class FixAllContext /// public Document Document => State.Document!; + /// + /// Original selection span with which fix all occurences was triggered. + /// + public TextSpan SelectionSpan => State.SelectionSpan; + /// /// Underlying which triggered this fix all. /// @@ -34,18 +48,9 @@ public sealed class FixAllContext public FixAllScope Scope => State.FixAllScope; /// - /// Optional span within the to fix all occurrences. - /// This span can be non-null only when is any of the following document based scopes: - /// 1. - /// 2. - /// 3. + /// The value expected of a participating in this fix all. /// - public TextSpan? FixAllSpan => State.FixAllSpan; - - /// - /// The underlying for the code refactoring for which fix all occurences was triggered. - /// - public CodeAction CodeAction => State.CodeAction; + public string? CodeActionEquivalenceKey => State.CodeAction.EquivalenceKey; /// /// CancellationToken for fix all session. @@ -85,6 +90,12 @@ public FixAllContext WithCancellationToken(CancellationToken cancellationToken) return new FixAllContext(State, this.ProgressTracker, cancellationToken); } + /// + /// Gets the spans to fix by document for the for this fix all occurences fix. + /// + public Task>> GetFixAllSpansAsync(CancellationToken cancellationToken) + => State.GetFixAllSpansAsync(cancellationToken); + internal FixAllContext WithDocument(Document? document) => this.WithState(State.WithDocument(document)); diff --git a/src/Workspaces/Core/Portable/CodeRefactorings/FixAllOccurences/FixAllContextHelper.cs b/src/Workspaces/Core/Portable/CodeRefactorings/FixAllOccurences/FixAllContextHelper.cs index 687a6d3e21161..8f5b32fc60a50 100644 --- a/src/Workspaces/Core/Portable/CodeRefactorings/FixAllOccurences/FixAllContextHelper.cs +++ b/src/Workspaces/Core/Portable/CodeRefactorings/FixAllOccurences/FixAllContextHelper.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using Microsoft.CodeAnalysis.CodeActions; -using Microsoft.CodeAnalysis.Shared.Utilities; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CodeRefactorings @@ -11,7 +10,7 @@ namespace Microsoft.CodeAnalysis.CodeRefactorings internal static class FixAllContextHelper { public static string GetDefaultFixAllTitle(FixAllContext fixAllContext) - => GetDefaultFixAllTitle(fixAllContext.Scope, fixAllContext.CodeAction, fixAllContext.Document, fixAllContext.Project); + => GetDefaultFixAllTitle(fixAllContext.Scope, fixAllContext.State.CodeAction, fixAllContext.Document, fixAllContext.Project); public static string GetDefaultFixAllTitle( FixAllScope fixAllScope, diff --git a/src/Workspaces/Core/Portable/CodeRefactorings/FixAllOccurences/FixAllProvider.cs b/src/Workspaces/Core/Portable/CodeRefactorings/FixAllOccurences/FixAllProvider.cs index 2542a61478de6..7483f938803b9 100644 --- a/src/Workspaces/Core/Portable/CodeRefactorings/FixAllOccurences/FixAllProvider.cs +++ b/src/Workspaces/Core/Portable/CodeRefactorings/FixAllOccurences/FixAllProvider.cs @@ -17,6 +17,9 @@ namespace Microsoft.CodeAnalysis.CodeRefactorings /// public abstract class FixAllProvider { + private protected static ImmutableArray DefaultSupportedFixAllScopes + = ImmutableArray.Create(FixAllScope.Document, FixAllScope.Project, FixAllScope.Solution); + /// /// Gets the supported scopes for applying multiple occurrences of a code refactoring. /// By default, it returns the following scopes: @@ -25,7 +28,7 @@ public abstract class FixAllProvider /// (c) /// public virtual IEnumerable GetSupportedFixAllScopes() - => ImmutableArray.Create(FixAllScope.Document, FixAllScope.Project, FixAllScope.Solution); + => DefaultSupportedFixAllScopes; /// /// Gets fix all occurrences fix for the given fixAllContext. @@ -43,40 +46,52 @@ public virtual IEnumerable GetSupportedFixAllScopes() /// of it (like attributes), or changes to the or it points at /// will be considered. /// - /// Indicates if is supported or not. - /// Indicates if is supported or not. + public static FixAllProvider Create(Func, Task> fixAllAsync) + => Create(fixAllAsync, DefaultSupportedFixAllScopes); + + /// + /// Create a that fixes documents independently. + /// This can be used in the case where refactoring(s) registered by this provider + /// only affect a single . + /// + /// + /// Callback that will apply the refactorings present in the provided document. The document returned will only be + /// examined for its content (e.g. it's or . No other aspects + /// of it (like attributes), or changes to the or it points at + /// will be considered. + /// + /// + /// Supported s for the fix all provider. + /// Note that is not supported by the + /// and should not be part of the supported scopes. + /// public static FixAllProvider Create( - Func> fixAllAsync, - bool supportsFixAllForContainingMember, - bool supportsFixAllForContainingType) + Func, Task> fixAllAsync!!, + ImmutableArray supportedFixAllScopes) { - if (fixAllAsync == null) - throw new ArgumentNullException(nameof(fixAllAsync)); + if (supportedFixAllScopes.IsDefault) + throw new ArgumentNullException(nameof(supportedFixAllScopes)); + + if (supportedFixAllScopes.Contains(FixAllScope.Custom)) + throw new ArgumentException(WorkspacesResources.FixAllScope_Custom_is_not_supported_with_this_API, nameof(supportedFixAllScopes)); - return new CallbackDocumentBasedFixAllProvider(fixAllAsync, - supportsFixAllForContainingMember, supportsFixAllForContainingType); + return new CallbackDocumentBasedFixAllProvider(fixAllAsync, supportedFixAllScopes); } private sealed class CallbackDocumentBasedFixAllProvider : DocumentBasedFixAllProvider { - private readonly Func> _fixAllAsync; + private readonly Func, Task> _fixAllAsync; public CallbackDocumentBasedFixAllProvider( - Func> fixAllAsync, - bool supportsFixAllForContainingMember, - bool supportsFixAllForContainingType) + Func, Task> fixAllAsync, + ImmutableArray supportedFixAllScopes) + : base(supportedFixAllScopes) { _fixAllAsync = fixAllAsync; - SupportsFixAllForContainingMember = supportsFixAllForContainingMember; - SupportsFixAllForContainingType = supportsFixAllForContainingType; } - protected override bool SupportsFixAllForContainingMember { get; } - - protected override bool SupportsFixAllForContainingType { get; } - - protected override Task FixAllAsync(FixAllContext context) - => _fixAllAsync(context); + protected override Task FixAllAsync(FixAllContext context, Document document, ImmutableArray fixAllSpans) + => _fixAllAsync(context, document, fixAllSpans); } } } diff --git a/src/Workspaces/Core/Portable/PublicAPI.Unshipped.txt b/src/Workspaces/Core/Portable/PublicAPI.Unshipped.txt index b8d584acf569e..0b5b483414780 100644 --- a/src/Workspaces/Core/Portable/PublicAPI.Unshipped.txt +++ b/src/Workspaces/Core/Portable/PublicAPI.Unshipped.txt @@ -1,21 +1,22 @@ +abstract Microsoft.CodeAnalysis.CodeRefactorings.DocumentBasedFixAllProvider.FixAllAsync(Microsoft.CodeAnalysis.CodeRefactorings.FixAllContext fixAllContext, Microsoft.CodeAnalysis.Document document, System.Collections.Immutable.ImmutableArray fixAllSpans) -> System.Threading.Tasks.Task Microsoft.CodeAnalysis.CodeFixes.DocumentBasedFixAllProvider.DocumentBasedFixAllProvider(System.Collections.Immutable.ImmutableArray supportedFixAllScopes) -> void Microsoft.CodeAnalysis.CodeFixes.FixAllContext.FixAllContext(Microsoft.CodeAnalysis.Document document, Microsoft.CodeAnalysis.Text.TextSpan? diagnosticSpan, Microsoft.CodeAnalysis.CodeFixes.CodeFixProvider codeFixProvider, Microsoft.CodeAnalysis.CodeFixes.FixAllScope scope, string codeActionEquivalenceKey, System.Collections.Generic.IEnumerable diagnosticIds, Microsoft.CodeAnalysis.CodeFixes.FixAllContext.DiagnosticProvider fixAllDiagnosticProvider, System.Threading.CancellationToken cancellationToken) -> void Microsoft.CodeAnalysis.CodeFixes.FixAllScope.ContainingMember = 4 -> Microsoft.CodeAnalysis.CodeFixes.FixAllScope Microsoft.CodeAnalysis.CodeFixes.FixAllScope.ContainingType = 5 -> Microsoft.CodeAnalysis.CodeFixes.FixAllScope -abstract Microsoft.CodeAnalysis.CodeRefactorings.DocumentBasedFixAllProvider.FixAllAsync(Microsoft.CodeAnalysis.CodeRefactorings.FixAllContext fixAllContext) -> System.Threading.Tasks.Task abstract Microsoft.CodeAnalysis.CodeRefactorings.DocumentBasedFixAllProvider.SupportsFixAllForContainingMember.get -> bool abstract Microsoft.CodeAnalysis.CodeRefactorings.DocumentBasedFixAllProvider.SupportsFixAllForContainingType.get -> bool -abstract Microsoft.CodeAnalysis.CodeRefactorings.DocumentBasedFixAllProvider.SupportsFixAllForSelection.get -> bool abstract Microsoft.CodeAnalysis.CodeRefactorings.FixAllProvider.GetFixAsync(Microsoft.CodeAnalysis.CodeRefactorings.FixAllContext fixAllContext) -> System.Threading.Tasks.Task Microsoft.CodeAnalysis.CodeRefactorings.DocumentBasedFixAllProvider Microsoft.CodeAnalysis.CodeRefactorings.DocumentBasedFixAllProvider.DocumentBasedFixAllProvider() -> void +Microsoft.CodeAnalysis.CodeRefactorings.DocumentBasedFixAllProvider.DocumentBasedFixAllProvider(System.Collections.Immutable.ImmutableArray supportedFixAllScopes) -> void Microsoft.CodeAnalysis.CodeRefactorings.FixAllContext Microsoft.CodeAnalysis.CodeRefactorings.FixAllContext.CancellationToken.get -> System.Threading.CancellationToken -Microsoft.CodeAnalysis.CodeRefactorings.FixAllContext.CodeAction.get -> Microsoft.CodeAnalysis.CodeActions.CodeAction +Microsoft.CodeAnalysis.CodeRefactorings.FixAllContext.CodeActionEquivalenceKey.get -> string Microsoft.CodeAnalysis.CodeRefactorings.FixAllContext.CodeRefactoringProvider.get -> Microsoft.CodeAnalysis.CodeRefactorings.CodeRefactoringProvider Microsoft.CodeAnalysis.CodeRefactorings.FixAllContext.Document.get -> Microsoft.CodeAnalysis.Document -Microsoft.CodeAnalysis.CodeRefactorings.FixAllContext.FixAllSpan.get -> Microsoft.CodeAnalysis.Text.TextSpan? +Microsoft.CodeAnalysis.CodeRefactorings.FixAllContext.GetFixAllSpansAsync(System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task>> Microsoft.CodeAnalysis.CodeRefactorings.FixAllContext.Scope.get -> Microsoft.CodeAnalysis.CodeRefactorings.FixAllScope +Microsoft.CodeAnalysis.CodeRefactorings.FixAllContext.SelectionSpan.get -> Microsoft.CodeAnalysis.Text.TextSpan Microsoft.CodeAnalysis.CodeRefactorings.FixAllContext.WithCancellationToken(System.Threading.CancellationToken cancellationToken) -> Microsoft.CodeAnalysis.CodeRefactorings.FixAllContext Microsoft.CodeAnalysis.CodeRefactorings.FixAllProvider Microsoft.CodeAnalysis.CodeRefactorings.FixAllProvider.FixAllProvider() -> void @@ -32,7 +33,8 @@ Microsoft.CodeAnalysis.Editing.SyntaxEditor.SyntaxEditor(Microsoft.CodeAnalysis. static Microsoft.CodeAnalysis.CodeFixes.FixAllProvider.Create(System.Func, System.Threading.Tasks.Task> fixAllAsync, System.Collections.Immutable.ImmutableArray supportedFixAllScopes) -> Microsoft.CodeAnalysis.CodeFixes.FixAllProvider override sealed Microsoft.CodeAnalysis.CodeRefactorings.DocumentBasedFixAllProvider.GetFixAsync(Microsoft.CodeAnalysis.CodeRefactorings.FixAllContext fixAllContext) -> System.Threading.Tasks.Task override sealed Microsoft.CodeAnalysis.CodeRefactorings.DocumentBasedFixAllProvider.GetSupportedFixAllScopes() -> System.Collections.Generic.IEnumerable -static Microsoft.CodeAnalysis.CodeRefactorings.FixAllProvider.Create(System.Func> fixAllAsync, bool supportsFixAllForContainingMember, bool supportsFixAllForContainingType) -> Microsoft.CodeAnalysis.CodeRefactorings.FixAllProvider +static Microsoft.CodeAnalysis.CodeRefactorings.FixAllProvider.Create(System.Func, System.Threading.Tasks.Task> fixAllAsync) -> Microsoft.CodeAnalysis.CodeRefactorings.FixAllProvider +static Microsoft.CodeAnalysis.CodeRefactorings.FixAllProvider.Create(System.Func, System.Threading.Tasks.Task> fixAllAsync, System.Collections.Immutable.ImmutableArray supportedFixAllScopes) -> Microsoft.CodeAnalysis.CodeRefactorings.FixAllProvider static readonly Microsoft.CodeAnalysis.Editing.SyntaxGenerator.DefaultRemoveOptions -> Microsoft.CodeAnalysis.SyntaxRemoveOptions Microsoft.CodeAnalysis.Rename.SymbolRenameOptions Microsoft.CodeAnalysis.Rename.SymbolRenameOptions.SymbolRenameOptions() -> void diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeRefactorings/FixAll/FixAllState.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeRefactorings/FixAll/FixAllState.cs index dc9cfb44f0964..695b819bdf8c4 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeRefactorings/FixAll/FixAllState.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeRefactorings/FixAll/FixAllState.cs @@ -3,8 +3,17 @@ // See the LICENSE file in the project root for more information. using System; +using System.Collections; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeActions; +using Microsoft.CodeAnalysis.FixAll; using Microsoft.CodeAnalysis.Internal.Log; +using Microsoft.CodeAnalysis.PooledObjects; +using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; @@ -20,57 +29,53 @@ internal partial class FixAllState public Document? Document { get; } public Project Project { get; } public FixAllScope FixAllScope { get; } - public TextSpan? FixAllSpan { get; } public Solution Solution => this.Project.Solution; + /// + /// Original selection span from which FixAll was invoked + /// + public TextSpan SelectionSpan { get; } + internal FixAllState( FixAllProvider fixAllProvider, - Document document, + Document document!!, + TextSpan selectionSpan, CodeRefactoringProvider codeRefactoringProvider, FixAllScope fixAllScope, - TextSpan? fixAllSpan, CodeAction codeAction) - : this(fixAllProvider, document, document.Project, codeRefactoringProvider, fixAllScope, fixAllSpan, codeAction) + : this(fixAllProvider, document, document.Project, selectionSpan, codeRefactoringProvider, fixAllScope, codeAction) { - if (document == null) - { - throw new ArgumentNullException(nameof(document)); - } } internal FixAllState( FixAllProvider fixAllProvider, - Project project, + Project project!!, + TextSpan selectionSpan, CodeRefactoringProvider codeRefactoringProvider, FixAllScope fixAllScope, CodeAction codeAction) - : this(fixAllProvider, document: null, project, codeRefactoringProvider, fixAllScope, fixAllSpan: null, codeAction) + : this(fixAllProvider, document: null, project, selectionSpan, codeRefactoringProvider, fixAllScope, codeAction) { - if (project == null) - { - throw new ArgumentNullException(nameof(project)); - } } private FixAllState( FixAllProvider fixAllProvider, Document? document, Project project, + TextSpan selectionSpan, CodeRefactoringProvider codeRefactoringProvider, FixAllScope fixAllScope, - TextSpan? fixAllSpan, CodeAction codeAction) { Contract.ThrowIfNull(project); - Contract.ThrowIfFalse(!fixAllSpan.HasValue || fixAllScope is FixAllScope.Document or - FixAllScope.ContainingMember or FixAllScope.ContainingType); + Contract.ThrowIfNull(codeRefactoringProvider); this.FixAllProvider = fixAllProvider; this.Document = document; this.Project = project; - this.CodeRefactoringProvider = codeRefactoringProvider ?? throw new ArgumentNullException(nameof(codeRefactoringProvider)); + this.SelectionSpan = selectionSpan; + this.CodeRefactoringProvider = codeRefactoringProvider; this.FixAllScope = fixAllScope; - this.FixAllSpan = fixAllSpan; this.CodeAction = codeAction; } @@ -78,7 +83,7 @@ public FixAllState WithDocument(Document? document) => this.With(document: document); public FixAllState WithProject(Project project) - => this.With(project: project, fixAllSpan: null); + => this.With(project: project); public FixAllState WithScope(FixAllScope scope) => this.With(scope: scope); @@ -86,18 +91,15 @@ public FixAllState WithScope(FixAllScope scope) public FixAllState With( Optional document = default, Optional project = default, - Optional scope = default, - Optional fixAllSpan = default) + Optional scope = default) { var newDocument = document.HasValue ? document.Value : this.Document; var newProject = project.HasValue ? project.Value : this.Project; var newFixAllScope = scope.HasValue ? scope.Value : this.FixAllScope; - var newFixAllSpan = fixAllSpan.HasValue ? fixAllSpan.Value : this.FixAllSpan; if (newDocument == this.Document && newProject == this.Project && - newFixAllScope == this.FixAllScope && - newFixAllSpan == this.FixAllSpan) + newFixAllScope == this.FixAllScope) { return this; } @@ -106,10 +108,64 @@ public FixAllState With( this.FixAllProvider, newDocument, newProject, + this.SelectionSpan, this.CodeRefactoringProvider, newFixAllScope, - newFixAllSpan, this.CodeAction); } + + /// + /// Gets the spans to fix by document for the for this fix all occurences fix. + /// + internal async Task>> GetFixAllSpansAsync(CancellationToken cancellationToken) + { + IEnumerable? documentsToFix = null; + switch (FixAllScope) + { + case FixAllScope.ContainingType or FixAllScope.ContainingMember: + Contract.ThrowIfNull(Document); + var spanMappingService = Document.GetLanguageService(); + if (spanMappingService is null) + return ImmutableDictionary>.Empty; + + return await spanMappingService.GetFixAllSpansAsync( + Document, SelectionSpan, FixAllScope, cancellationToken).ConfigureAwait(false); + + case FixAllScope.Document: + Contract.ThrowIfNull(Document); + documentsToFix = SpecializedCollections.SingletonEnumerable(Document); + break; + + case FixAllScope.Project: + documentsToFix = Project.Documents; + break; + + case FixAllScope.Solution: + documentsToFix = Project.Solution.Projects.SelectMany(p => p.Documents); + break; + + default: + return ImmutableDictionary>.Empty; + } + + using var _ = PooledDictionary>.GetInstance(out var builder); + foreach (var document in documentsToFix) + { + TextSpan span; + if (document.SupportsSyntaxTree) + { + var root = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); + span = root.FullSpan; + } + else + { + span = default; + } + + builder.Add(document, ImmutableArray.Create(span)); + } + + return builder.ToImmutableDictionary(); + } } } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeRefactorings/SyntaxEditorBasedCodeRefactoringProvider.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeRefactorings/SyntaxEditorBasedCodeRefactoringProvider.cs index 6ed16a8cbe7ad..ca6fab67c7111 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeRefactorings/SyntaxEditorBasedCodeRefactoringProvider.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeRefactorings/SyntaxEditorBasedCodeRefactoringProvider.cs @@ -15,47 +15,39 @@ namespace Microsoft.CodeAnalysis.CodeRefactorings { internal abstract partial class SyntaxEditorBasedCodeRefactoringProvider : CodeRefactoringProvider { - private readonly bool _supportsFixAll; - private readonly bool _supportsFixAllForContainingMember; - private readonly bool _supportsFixAllForContainingType; + protected static readonly ImmutableArray DefaultFixAllScopes = ImmutableArray.Create(FixAllScope.Document, + FixAllScope.Project, FixAllScope.Solution); + protected static readonly ImmutableArray AllFixAllScopes = ImmutableArray.Create(FixAllScope.Document, + FixAllScope.Project, FixAllScope.Solution, FixAllScope.ContainingType, FixAllScope.ContainingMember); - protected SyntaxEditorBasedCodeRefactoringProvider( - bool supportsFixAll = true, - bool supportsFixAllForContainingMember = true, - bool supportsFixAllForContainingType = true) - { - _supportsFixAll = supportsFixAll; - _supportsFixAllForContainingMember = supportsFixAllForContainingMember; - _supportsFixAllForContainingType = supportsFixAllForContainingType; - } + protected abstract ImmutableArray SupportedFixAllScopes { get; } public sealed override FixAllProvider? GetFixAllProvider() { - if (!_supportsFixAll) + if (SupportedFixAllScopes.IsEmpty) return null; return FixAllProvider.Create( - async fixAllContext => + async (fixAllContext, document, fixAllSpans) => { - return await this.FixAllAsync(fixAllContext.Document, fixAllContext.FixAllSpan, fixAllContext.CodeAction, fixAllContext.CancellationToken).ConfigureAwait(false); + return await this.FixAllAsync(document, fixAllSpans, fixAllContext.CancellationToken).ConfigureAwait(false); }, - _supportsFixAllForContainingMember, - _supportsFixAllForContainingType); + SupportedFixAllScopes); } protected Task FixAsync( Document document, TextSpan fixAllSpan, CancellationToken cancellationToken) { return FixAllWithEditorAsync(document, - editor => FixAllAsync(document, fixAllSpan, originalCodeAction: null, editor, cancellationToken), + editor => FixAllAsync(document, ImmutableArray.Create(fixAllSpan), editor, cancellationToken), cancellationToken); } protected Task FixAllAsync( - Document document, TextSpan? fixAllSpan, CodeAction originalCodeAction, CancellationToken cancellationToken) + Document document, ImmutableArray fixAllSpans, CancellationToken cancellationToken) { return FixAllWithEditorAsync(document, - editor => FixAllAsync(document, fixAllSpan ?? editor.OriginalRoot.FullSpan, originalCodeAction, editor, cancellationToken), + editor => FixAllAsync(document, fixAllSpans, editor, cancellationToken), cancellationToken); } @@ -74,6 +66,6 @@ internal static async Task FixAllWithEditorAsync( } protected abstract Task FixAllAsync( - Document document, TextSpan fixAllSpan, CodeAction? originalCodeAction, SyntaxEditor editor, CancellationToken cancellationToken); + Document document, ImmutableArray fixAllSpans, SyntaxEditor editor, CancellationToken cancellationToken); } } From 6e86cd69c9a9b0d24306270aafc85f7ce291c6b6 Mon Sep 17 00:00:00 2001 From: Manish Vasani Date: Tue, 12 Apr 2022 03:12:48 -0700 Subject: [PATCH 12/18] Make FixAll APIs for refactorings internal for now. #60703 tracks making them public in future. Also move some helper types from shared layer to workspaces layer as a result of internalizing these APIs --- .../CSharpFixAllSpanMappingService.cs | 0 ...soft.CodeAnalysis.CSharp.Workspaces.csproj | 6 ++-- .../CodeRefactoringProvider.cs | 5 ++- .../DocumentBasedFixAllProvider.cs | 4 ++- .../FixAllOccurences/FixAllContext.cs | 5 ++- .../FixAllContextExtensions.cs | 0 .../FixAllOccurences/FixAllProvider.cs | 5 ++- .../FixAllOccurences/FixAllScope.cs | 5 ++- .../FixAllOccurences}/FixAllState.cs | 0 ...yntaxEditorBasedCodeRefactoringProvider.cs | 2 +- .../AbstractFixAllSpanMappingService.cs | 0 .../IFixAllSpanMappingService.cs | 0 .../Core/Portable/PublicAPI.Unshipped.txt | 32 ------------------- .../Shared/Extensions/TelemetryExtensions.cs | 3 +- .../CSharpWorkspaceExtensions.projitems | 1 - .../Core/WorkspaceExtensions.projitems | 5 --- .../VisualBasicWorkspaceExtensions.projitems | 1 - .../VisualBasicFixAllSpanMappingService.vb | 0 18 files changed, 25 insertions(+), 49 deletions(-) rename src/Workspaces/{SharedUtilitiesAndExtensions/Workspace/CSharp/LanguageServices => CSharp/Portable/LanguageServices/FixAllSpanMappingService}/CSharpFixAllSpanMappingService.cs (100%) rename src/Workspaces/{SharedUtilitiesAndExtensions/Workspace/Core/CodeRefactorings/FixAll => Core/Portable/CodeRefactorings/FixAllOccurences}/FixAllContextExtensions.cs (100%) rename src/Workspaces/{SharedUtilitiesAndExtensions/Workspace/Core/CodeRefactorings/FixAll => Core/Portable/CodeRefactorings/FixAllOccurences}/FixAllState.cs (100%) rename src/Workspaces/{SharedUtilitiesAndExtensions/Workspace/Core => Core/Portable}/CodeRefactorings/SyntaxEditorBasedCodeRefactoringProvider.cs (97%) rename src/Workspaces/{SharedUtilitiesAndExtensions/Workspace/Core => Core/Portable}/LanguageServices/FixAllSpanMappingService/AbstractFixAllSpanMappingService.cs (100%) rename src/Workspaces/{SharedUtilitiesAndExtensions/Workspace/Core => Core/Portable}/LanguageServices/FixAllSpanMappingService/IFixAllSpanMappingService.cs (100%) rename src/Workspaces/{SharedUtilitiesAndExtensions/Workspace/VisualBasic/LanguageServices => VisualBasic/Portable/LanguageServices/FixAllSpanMappingService}/VisualBasicFixAllSpanMappingService.vb (100%) diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/LanguageServices/CSharpFixAllSpanMappingService.cs b/src/Workspaces/CSharp/Portable/LanguageServices/FixAllSpanMappingService/CSharpFixAllSpanMappingService.cs similarity index 100% rename from src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/LanguageServices/CSharpFixAllSpanMappingService.cs rename to src/Workspaces/CSharp/Portable/LanguageServices/FixAllSpanMappingService/CSharpFixAllSpanMappingService.cs diff --git a/src/Workspaces/CSharp/Portable/Microsoft.CodeAnalysis.CSharp.Workspaces.csproj b/src/Workspaces/CSharp/Portable/Microsoft.CodeAnalysis.CSharp.Workspaces.csproj index 78f170de0e965..aa9d5c9d26272 100644 --- a/src/Workspaces/CSharp/Portable/Microsoft.CodeAnalysis.CSharp.Workspaces.csproj +++ b/src/Workspaces/CSharp/Portable/Microsoft.CodeAnalysis.CSharp.Workspaces.csproj @@ -46,9 +46,9 @@ - - - + + + diff --git a/src/Workspaces/Core/Portable/CodeRefactorings/CodeRefactoringProvider.cs b/src/Workspaces/Core/Portable/CodeRefactorings/CodeRefactoringProvider.cs index 9f345c0ec9145..59be660ac5e66 100644 --- a/src/Workspaces/Core/Portable/CodeRefactorings/CodeRefactoringProvider.cs +++ b/src/Workspaces/Core/Portable/CodeRefactorings/CodeRefactoringProvider.cs @@ -24,7 +24,10 @@ public abstract class CodeRefactoringProvider /// registered by this code refactoring provider across the supported s. /// Return null if the provider doesn't support fix all operation. /// - public virtual FixAllProvider? GetFixAllProvider() + /// + /// TODO: Make public, tracked with https://github.com/dotnet/roslyn/issues/60703 + /// + internal virtual FixAllProvider? GetFixAllProvider() => null; /// diff --git a/src/Workspaces/Core/Portable/CodeRefactorings/FixAllOccurences/DocumentBasedFixAllProvider.cs b/src/Workspaces/Core/Portable/CodeRefactorings/FixAllOccurences/DocumentBasedFixAllProvider.cs index 58da72b6e8c1e..675c13aeed0e0 100644 --- a/src/Workspaces/Core/Portable/CodeRefactorings/FixAllOccurences/DocumentBasedFixAllProvider.cs +++ b/src/Workspaces/Core/Portable/CodeRefactorings/FixAllOccurences/DocumentBasedFixAllProvider.cs @@ -27,8 +27,10 @@ namespace Microsoft.CodeAnalysis.CodeRefactorings /// This type provides suitable logic for fixing large solutions in an efficient manner. Projects are serially /// processed, with all the documents in the project being processed in parallel. /// is invoked for each document for implementors to process. + /// + /// TODO: Make public, tracked with https://github.com/dotnet/roslyn/issues/60703 /// - public abstract class DocumentBasedFixAllProvider : FixAllProvider + internal abstract class DocumentBasedFixAllProvider : FixAllProvider { private readonly ImmutableArray _supportedFixAllScopes; diff --git a/src/Workspaces/Core/Portable/CodeRefactorings/FixAllOccurences/FixAllContext.cs b/src/Workspaces/Core/Portable/CodeRefactorings/FixAllOccurences/FixAllContext.cs index 7e449ee1aa5ad..1a345fb8c9d17 100644 --- a/src/Workspaces/Core/Portable/CodeRefactorings/FixAllOccurences/FixAllContext.cs +++ b/src/Workspaces/Core/Portable/CodeRefactorings/FixAllOccurences/FixAllContext.cs @@ -21,7 +21,10 @@ namespace Microsoft.CodeAnalysis.CodeRefactorings /// /// Context for "Fix all occurrences" for code refactorings provided by each . /// - public sealed class FixAllContext + /// + /// TODO: Make public, tracked with https://github.com/dotnet/roslyn/issues/60703 + /// + internal sealed class FixAllContext { internal FixAllState State { get; } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeRefactorings/FixAll/FixAllContextExtensions.cs b/src/Workspaces/Core/Portable/CodeRefactorings/FixAllOccurences/FixAllContextExtensions.cs similarity index 100% rename from src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeRefactorings/FixAll/FixAllContextExtensions.cs rename to src/Workspaces/Core/Portable/CodeRefactorings/FixAllOccurences/FixAllContextExtensions.cs diff --git a/src/Workspaces/Core/Portable/CodeRefactorings/FixAllOccurences/FixAllProvider.cs b/src/Workspaces/Core/Portable/CodeRefactorings/FixAllOccurences/FixAllProvider.cs index 7483f938803b9..794d7d936b716 100644 --- a/src/Workspaces/Core/Portable/CodeRefactorings/FixAllOccurences/FixAllProvider.cs +++ b/src/Workspaces/Core/Portable/CodeRefactorings/FixAllOccurences/FixAllProvider.cs @@ -15,7 +15,10 @@ namespace Microsoft.CodeAnalysis.CodeRefactorings /// /// Implement this abstract type to provide fix all occurrences support for code refactorings. /// - public abstract class FixAllProvider + /// + /// TODO: Make public, tracked with https://github.com/dotnet/roslyn/issues/60703 + /// + internal abstract class FixAllProvider { private protected static ImmutableArray DefaultSupportedFixAllScopes = ImmutableArray.Create(FixAllScope.Document, FixAllScope.Project, FixAllScope.Solution); diff --git a/src/Workspaces/Core/Portable/CodeRefactorings/FixAllOccurences/FixAllScope.cs b/src/Workspaces/Core/Portable/CodeRefactorings/FixAllOccurences/FixAllScope.cs index 8fcb0e1748cc8..412dfbe58fb62 100644 --- a/src/Workspaces/Core/Portable/CodeRefactorings/FixAllOccurences/FixAllScope.cs +++ b/src/Workspaces/Core/Portable/CodeRefactorings/FixAllOccurences/FixAllScope.cs @@ -7,7 +7,10 @@ namespace Microsoft.CodeAnalysis.CodeRefactorings /// /// Indicates scope for "Fix all occurrences" for code refactorings provided by each . /// - public enum FixAllScope + /// /// + /// TODO: Make public, tracked with https://github.com/dotnet/roslyn/issues/60703 + /// + internal enum FixAllScope { Document, Project, diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeRefactorings/FixAll/FixAllState.cs b/src/Workspaces/Core/Portable/CodeRefactorings/FixAllOccurences/FixAllState.cs similarity index 100% rename from src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeRefactorings/FixAll/FixAllState.cs rename to src/Workspaces/Core/Portable/CodeRefactorings/FixAllOccurences/FixAllState.cs diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeRefactorings/SyntaxEditorBasedCodeRefactoringProvider.cs b/src/Workspaces/Core/Portable/CodeRefactorings/SyntaxEditorBasedCodeRefactoringProvider.cs similarity index 97% rename from src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeRefactorings/SyntaxEditorBasedCodeRefactoringProvider.cs rename to src/Workspaces/Core/Portable/CodeRefactorings/SyntaxEditorBasedCodeRefactoringProvider.cs index ca6fab67c7111..dee982c51c236 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeRefactorings/SyntaxEditorBasedCodeRefactoringProvider.cs +++ b/src/Workspaces/Core/Portable/CodeRefactorings/SyntaxEditorBasedCodeRefactoringProvider.cs @@ -22,7 +22,7 @@ internal abstract partial class SyntaxEditorBasedCodeRefactoringProvider : CodeR protected abstract ImmutableArray SupportedFixAllScopes { get; } - public sealed override FixAllProvider? GetFixAllProvider() + internal sealed override FixAllProvider? GetFixAllProvider() { if (SupportedFixAllScopes.IsEmpty) return null; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/LanguageServices/FixAllSpanMappingService/AbstractFixAllSpanMappingService.cs b/src/Workspaces/Core/Portable/LanguageServices/FixAllSpanMappingService/AbstractFixAllSpanMappingService.cs similarity index 100% rename from src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/LanguageServices/FixAllSpanMappingService/AbstractFixAllSpanMappingService.cs rename to src/Workspaces/Core/Portable/LanguageServices/FixAllSpanMappingService/AbstractFixAllSpanMappingService.cs diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/LanguageServices/FixAllSpanMappingService/IFixAllSpanMappingService.cs b/src/Workspaces/Core/Portable/LanguageServices/FixAllSpanMappingService/IFixAllSpanMappingService.cs similarity index 100% rename from src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/LanguageServices/FixAllSpanMappingService/IFixAllSpanMappingService.cs rename to src/Workspaces/Core/Portable/LanguageServices/FixAllSpanMappingService/IFixAllSpanMappingService.cs diff --git a/src/Workspaces/Core/Portable/PublicAPI.Unshipped.txt b/src/Workspaces/Core/Portable/PublicAPI.Unshipped.txt index 0b5b483414780..a91df034f268a 100644 --- a/src/Workspaces/Core/Portable/PublicAPI.Unshipped.txt +++ b/src/Workspaces/Core/Portable/PublicAPI.Unshipped.txt @@ -1,40 +1,10 @@ -abstract Microsoft.CodeAnalysis.CodeRefactorings.DocumentBasedFixAllProvider.FixAllAsync(Microsoft.CodeAnalysis.CodeRefactorings.FixAllContext fixAllContext, Microsoft.CodeAnalysis.Document document, System.Collections.Immutable.ImmutableArray fixAllSpans) -> System.Threading.Tasks.Task Microsoft.CodeAnalysis.CodeFixes.DocumentBasedFixAllProvider.DocumentBasedFixAllProvider(System.Collections.Immutable.ImmutableArray supportedFixAllScopes) -> void Microsoft.CodeAnalysis.CodeFixes.FixAllContext.FixAllContext(Microsoft.CodeAnalysis.Document document, Microsoft.CodeAnalysis.Text.TextSpan? diagnosticSpan, Microsoft.CodeAnalysis.CodeFixes.CodeFixProvider codeFixProvider, Microsoft.CodeAnalysis.CodeFixes.FixAllScope scope, string codeActionEquivalenceKey, System.Collections.Generic.IEnumerable diagnosticIds, Microsoft.CodeAnalysis.CodeFixes.FixAllContext.DiagnosticProvider fixAllDiagnosticProvider, System.Threading.CancellationToken cancellationToken) -> void Microsoft.CodeAnalysis.CodeFixes.FixAllScope.ContainingMember = 4 -> Microsoft.CodeAnalysis.CodeFixes.FixAllScope Microsoft.CodeAnalysis.CodeFixes.FixAllScope.ContainingType = 5 -> Microsoft.CodeAnalysis.CodeFixes.FixAllScope -abstract Microsoft.CodeAnalysis.CodeRefactorings.DocumentBasedFixAllProvider.SupportsFixAllForContainingMember.get -> bool -abstract Microsoft.CodeAnalysis.CodeRefactorings.DocumentBasedFixAllProvider.SupportsFixAllForContainingType.get -> bool -abstract Microsoft.CodeAnalysis.CodeRefactorings.FixAllProvider.GetFixAsync(Microsoft.CodeAnalysis.CodeRefactorings.FixAllContext fixAllContext) -> System.Threading.Tasks.Task -Microsoft.CodeAnalysis.CodeRefactorings.DocumentBasedFixAllProvider -Microsoft.CodeAnalysis.CodeRefactorings.DocumentBasedFixAllProvider.DocumentBasedFixAllProvider() -> void -Microsoft.CodeAnalysis.CodeRefactorings.DocumentBasedFixAllProvider.DocumentBasedFixAllProvider(System.Collections.Immutable.ImmutableArray supportedFixAllScopes) -> void -Microsoft.CodeAnalysis.CodeRefactorings.FixAllContext -Microsoft.CodeAnalysis.CodeRefactorings.FixAllContext.CancellationToken.get -> System.Threading.CancellationToken -Microsoft.CodeAnalysis.CodeRefactorings.FixAllContext.CodeActionEquivalenceKey.get -> string -Microsoft.CodeAnalysis.CodeRefactorings.FixAllContext.CodeRefactoringProvider.get -> Microsoft.CodeAnalysis.CodeRefactorings.CodeRefactoringProvider -Microsoft.CodeAnalysis.CodeRefactorings.FixAllContext.Document.get -> Microsoft.CodeAnalysis.Document -Microsoft.CodeAnalysis.CodeRefactorings.FixAllContext.GetFixAllSpansAsync(System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task>> -Microsoft.CodeAnalysis.CodeRefactorings.FixAllContext.Scope.get -> Microsoft.CodeAnalysis.CodeRefactorings.FixAllScope -Microsoft.CodeAnalysis.CodeRefactorings.FixAllContext.SelectionSpan.get -> Microsoft.CodeAnalysis.Text.TextSpan -Microsoft.CodeAnalysis.CodeRefactorings.FixAllContext.WithCancellationToken(System.Threading.CancellationToken cancellationToken) -> Microsoft.CodeAnalysis.CodeRefactorings.FixAllContext -Microsoft.CodeAnalysis.CodeRefactorings.FixAllProvider -Microsoft.CodeAnalysis.CodeRefactorings.FixAllProvider.FixAllProvider() -> void -Microsoft.CodeAnalysis.CodeRefactorings.FixAllScope -Microsoft.CodeAnalysis.CodeRefactorings.FixAllScope.ContainingMember = 4 -> Microsoft.CodeAnalysis.CodeRefactorings.FixAllScope -Microsoft.CodeAnalysis.CodeRefactorings.FixAllScope.ContainingType = 5 -> Microsoft.CodeAnalysis.CodeRefactorings.FixAllScope -Microsoft.CodeAnalysis.CodeRefactorings.FixAllScope.Custom = 2147483647 -> Microsoft.CodeAnalysis.CodeRefactorings.FixAllScope -Microsoft.CodeAnalysis.CodeRefactorings.FixAllScope.Document = 0 -> Microsoft.CodeAnalysis.CodeRefactorings.FixAllScope -Microsoft.CodeAnalysis.CodeRefactorings.FixAllScope.Project = 1 -> Microsoft.CodeAnalysis.CodeRefactorings.FixAllScope -Microsoft.CodeAnalysis.CodeRefactorings.FixAllScope.Selection = 3 -> Microsoft.CodeAnalysis.CodeRefactorings.FixAllScope -Microsoft.CodeAnalysis.CodeRefactorings.FixAllScope.Solution = 2 -> Microsoft.CodeAnalysis.CodeRefactorings.FixAllScope Microsoft.CodeAnalysis.Editing.SyntaxEditor.SyntaxEditor(Microsoft.CodeAnalysis.SyntaxNode root, Microsoft.CodeAnalysis.Host.HostWorkspaceServices services) -> void *REMOVED*static Microsoft.CodeAnalysis.Editing.SyntaxGenerator.DefaultRemoveOptions -> Microsoft.CodeAnalysis.SyntaxRemoveOptions static Microsoft.CodeAnalysis.CodeFixes.FixAllProvider.Create(System.Func, System.Threading.Tasks.Task> fixAllAsync, System.Collections.Immutable.ImmutableArray supportedFixAllScopes) -> Microsoft.CodeAnalysis.CodeFixes.FixAllProvider -override sealed Microsoft.CodeAnalysis.CodeRefactorings.DocumentBasedFixAllProvider.GetFixAsync(Microsoft.CodeAnalysis.CodeRefactorings.FixAllContext fixAllContext) -> System.Threading.Tasks.Task -override sealed Microsoft.CodeAnalysis.CodeRefactorings.DocumentBasedFixAllProvider.GetSupportedFixAllScopes() -> System.Collections.Generic.IEnumerable -static Microsoft.CodeAnalysis.CodeRefactorings.FixAllProvider.Create(System.Func, System.Threading.Tasks.Task> fixAllAsync) -> Microsoft.CodeAnalysis.CodeRefactorings.FixAllProvider -static Microsoft.CodeAnalysis.CodeRefactorings.FixAllProvider.Create(System.Func, System.Threading.Tasks.Task> fixAllAsync, System.Collections.Immutable.ImmutableArray supportedFixAllScopes) -> Microsoft.CodeAnalysis.CodeRefactorings.FixAllProvider static readonly Microsoft.CodeAnalysis.Editing.SyntaxGenerator.DefaultRemoveOptions -> Microsoft.CodeAnalysis.SyntaxRemoveOptions Microsoft.CodeAnalysis.Rename.SymbolRenameOptions Microsoft.CodeAnalysis.Rename.SymbolRenameOptions.SymbolRenameOptions() -> void @@ -56,5 +26,3 @@ Microsoft.CodeAnalysis.Rename.DocumentRenameOptions.RenameMatchingTypeInStrings. Microsoft.CodeAnalysis.Rename.DocumentRenameOptions.RenameMatchingTypeInComments.init -> void static Microsoft.CodeAnalysis.Rename.Renamer.RenameDocumentAsync(Microsoft.CodeAnalysis.Document document, Microsoft.CodeAnalysis.Rename.DocumentRenameOptions options, string newDocumentName, System.Collections.Generic.IReadOnlyList newDocumentFolders = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task static Microsoft.CodeAnalysis.Rename.Renamer.RenameSymbolAsync(Microsoft.CodeAnalysis.Solution solution, Microsoft.CodeAnalysis.ISymbol symbol, Microsoft.CodeAnalysis.Rename.SymbolRenameOptions options, string newName, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task -virtual Microsoft.CodeAnalysis.CodeRefactorings.DocumentBasedFixAllProvider.GetFixAllTitle(Microsoft.CodeAnalysis.CodeRefactorings.FixAllContext fixAllContext) -> string -virtual Microsoft.CodeAnalysis.CodeRefactorings.FixAllProvider.GetSupportedFixAllScopes() -> System.Collections.Generic.IEnumerable diff --git a/src/Workspaces/Core/Portable/Shared/Extensions/TelemetryExtensions.cs b/src/Workspaces/Core/Portable/Shared/Extensions/TelemetryExtensions.cs index d7e4cb2c71fba..29b41bf8bed18 100644 --- a/src/Workspaces/Core/Portable/Shared/Extensions/TelemetryExtensions.cs +++ b/src/Workspaces/Core/Portable/Shared/Extensions/TelemetryExtensions.cs @@ -50,6 +50,7 @@ public static short GetScopeIdForTelemetry(this FixAllScope scope) _ => 7, }; +#if WORKSPACE public static short GetScopeIdForTelemetry(this CodeRefactorings.FixAllScope scope) => scope switch { @@ -60,7 +61,7 @@ public static short GetScopeIdForTelemetry(this CodeRefactorings.FixAllScope sco CodeRefactorings.FixAllScope.ContainingType => 5, _ => short.MaxValue, }; - +#endif public static string GetTelemetryDiagnosticID(this Diagnostic diagnostic) { // we log diagnostic id as it is if it is from us diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/CSharpWorkspaceExtensions.projitems b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/CSharpWorkspaceExtensions.projitems index 3f4c7e69240df..f3148e719a940 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/CSharpWorkspaceExtensions.projitems +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/CSharpWorkspaceExtensions.projitems @@ -43,7 +43,6 @@ - diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/WorkspaceExtensions.projitems b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/WorkspaceExtensions.projitems index 4c8126c8d2815..2155ffde7f723 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/WorkspaceExtensions.projitems +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/WorkspaceExtensions.projitems @@ -12,9 +12,6 @@ - - - @@ -40,8 +37,6 @@ - - diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/VisualBasic/VisualBasicWorkspaceExtensions.projitems b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/VisualBasic/VisualBasicWorkspaceExtensions.projitems index a93de403add5d..188cd186e1024 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/VisualBasic/VisualBasicWorkspaceExtensions.projitems +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/VisualBasic/VisualBasicWorkspaceExtensions.projitems @@ -43,7 +43,6 @@ - diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/VisualBasic/LanguageServices/VisualBasicFixAllSpanMappingService.vb b/src/Workspaces/VisualBasic/Portable/LanguageServices/FixAllSpanMappingService/VisualBasicFixAllSpanMappingService.vb similarity index 100% rename from src/Workspaces/SharedUtilitiesAndExtensions/Workspace/VisualBasic/LanguageServices/VisualBasicFixAllSpanMappingService.vb rename to src/Workspaces/VisualBasic/Portable/LanguageServices/FixAllSpanMappingService/VisualBasicFixAllSpanMappingService.vb From b27b6ca90a41c3ebf9a8a07873b947d66d2558eb Mon Sep 17 00:00:00 2001 From: Manish Vasani Date: Tue, 12 Apr 2022 03:20:07 -0700 Subject: [PATCH 13/18] Fix function ID --- .../SuggestedActions/FixAllCodeRefactoringSuggestedAction.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/EditorFeatures/Core.Wpf/Suggestions/SuggestedActions/FixAllCodeRefactoringSuggestedAction.cs b/src/EditorFeatures/Core.Wpf/Suggestions/SuggestedActions/FixAllCodeRefactoringSuggestedAction.cs index 62d02ffb181bc..7bad1bdf60c1f 100644 --- a/src/EditorFeatures/Core.Wpf/Suggestions/SuggestedActions/FixAllCodeRefactoringSuggestedAction.cs +++ b/src/EditorFeatures/Core.Wpf/Suggestions/SuggestedActions/FixAllCodeRefactoringSuggestedAction.cs @@ -57,7 +57,7 @@ protected override async Task InnerInvokeAsync( { await this.ThreadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); - using (Logger.LogBlock(FunctionId.CodeFixes_FixAllOccurrencesSession, FixAllLogger.CreateCorrelationLogMessage(FixAllState.CorrelationId), cancellationToken)) + using (Logger.LogBlock(FunctionId.Refactoring_FixAllOccurrencesSession, FixAllLogger.CreateCorrelationLogMessage(FixAllState.CorrelationId), cancellationToken)) { await base.InnerInvokeAsync(progressTracker, cancellationToken).ConfigureAwait(false); } From 09d595c8c7cf4331971699c2e264199de0a5342f Mon Sep 17 00:00:00 2001 From: Manish Vasani Date: Tue, 12 Apr 2022 03:34:23 -0700 Subject: [PATCH 14/18] Fixes --- .../FixAllOccurences/FixAllContextHelper.cs | 8 ++++---- .../CodeRefactorings/FixAllOccurences/FixAllLogger.cs | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/Workspaces/Core/Portable/CodeRefactorings/FixAllOccurences/FixAllContextHelper.cs b/src/Workspaces/Core/Portable/CodeRefactorings/FixAllOccurences/FixAllContextHelper.cs index 8f5b32fc60a50..4971e6fdff7b3 100644 --- a/src/Workspaces/Core/Portable/CodeRefactorings/FixAllOccurences/FixAllContextHelper.cs +++ b/src/Workspaces/Core/Portable/CodeRefactorings/FixAllOccurences/FixAllContextHelper.cs @@ -15,18 +15,18 @@ public static string GetDefaultFixAllTitle(FixAllContext fixAllContext) public static string GetDefaultFixAllTitle( FixAllScope fixAllScope, CodeAction codeAction, - Document? triggerDocument, + Document triggerDocument, Project triggerProject) { var title = codeAction.Title; return fixAllScope switch { FixAllScope.Custom => string.Format(WorkspaceExtensionsResources.Fix_all_0, title), - FixAllScope.Document => string.Format(WorkspaceExtensionsResources.Fix_all_0_in_1, title, triggerDocument!.Name), + FixAllScope.Document => string.Format(WorkspaceExtensionsResources.Fix_all_0_in_1, title, triggerDocument.Name), FixAllScope.Project => string.Format(WorkspaceExtensionsResources.Fix_all_0_in_1, title, triggerProject.Name), FixAllScope.Solution => string.Format(WorkspaceExtensionsResources.Fix_all_0_in_Solution, title), - FixAllScope.ContainingMember => string.Format(WorkspaceExtensionsResources.Fix_all_0_in_containing_member_for_1, title, triggerDocument!.Name), - FixAllScope.ContainingType => string.Format(WorkspaceExtensionsResources.Fix_all_0_in_containing_type_for_1, title, triggerDocument!.Name), + FixAllScope.ContainingMember => string.Format(WorkspaceExtensionsResources.Fix_all_0_in_containing_member_for_1, title, triggerDocument.Name), + FixAllScope.ContainingType => string.Format(WorkspaceExtensionsResources.Fix_all_0_in_containing_type_for_1, title, triggerDocument.Name), _ => throw ExceptionUtilities.UnexpectedValue(fixAllScope), }; } diff --git a/src/Workspaces/Core/Portable/CodeRefactorings/FixAllOccurences/FixAllLogger.cs b/src/Workspaces/Core/Portable/CodeRefactorings/FixAllOccurences/FixAllLogger.cs index 05f0d8091e44f..321c25152424f 100644 --- a/src/Workspaces/Core/Portable/CodeRefactorings/FixAllOccurences/FixAllLogger.cs +++ b/src/Workspaces/Core/Portable/CodeRefactorings/FixAllOccurences/FixAllLogger.cs @@ -32,13 +32,13 @@ internal static class FixAllLogger private const string AllChangesApplied = nameof(AllChangesApplied); private const string SubsetOfChangesApplied = nameof(SubsetOfChangesApplied); - public static void LogState(FixAllState fixAllState, bool isInternalCodeFixProvider) + public static void LogState(FixAllState fixAllState, bool isInternalCodeRefactoringProvider) { - Logger.Log(FunctionId.CodeFixes_FixAllOccurrencesContext, KeyValueLogMessage.Create(m => + Logger.Log(FunctionId.Refactoring_FixAllOccurrencesContext, KeyValueLogMessage.Create(m => { m[CorrelationId] = fixAllState.CorrelationId; - if (isInternalCodeFixProvider) + if (isInternalCodeRefactoringProvider) { m[CodeRefactoringProvider] = fixAllState.CodeRefactoringProvider.GetType().FullName!; m[CodeActionEquivalenceKey] = fixAllState.CodeAction.EquivalenceKey; From b2b7cf8abcb83c5cd52a85b315c5f776f6253384 Mon Sep 17 00:00:00 2001 From: Manish Vasani Date: Wed, 13 Apr 2022 04:17:50 -0700 Subject: [PATCH 15/18] Add unit tests and integration tests --- .../InvertConditionalTests_FixAll.cs | 412 ++++++++++++++++++ .../AbstractCodeActionOrUserDiagnosticTest.cs | 86 ++++ .../CodeActions/AbstractCodeActionTest.cs | 59 ++- ...agnosticProviderBasedUserDiagnosticTest.cs | 6 +- .../AbstractSuppressionDiagnosticTest.cs | 6 +- ...ractUnncessarySuppressionDiagnosticTest.cs | 6 +- .../Diagnostics/AbstractUserDiagnosticTest.cs | 57 +-- .../InvertConditional/InvertIfTests_FixAll.vb | 368 ++++++++++++++++ .../CSharp/CSharpCodeActions.cs | 214 ++++++++- 9 files changed, 1132 insertions(+), 82 deletions(-) create mode 100644 src/EditorFeatures/CSharpTest/InvertConditional/InvertConditionalTests_FixAll.cs create mode 100644 src/EditorFeatures/VisualBasicTest/InvertConditional/InvertIfTests_FixAll.vb diff --git a/src/EditorFeatures/CSharpTest/InvertConditional/InvertConditionalTests_FixAll.cs b/src/EditorFeatures/CSharpTest/InvertConditional/InvertConditionalTests_FixAll.cs new file mode 100644 index 0000000000000..db4852821cef7 --- /dev/null +++ b/src/EditorFeatures/CSharpTest/InvertConditional/InvertConditionalTests_FixAll.cs @@ -0,0 +1,412 @@ +// 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.Threading.Tasks; +using Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.CodeRefactorings; +using Microsoft.CodeAnalysis.Test.Utilities; +using Xunit; + +namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.InvertConditional +{ + [Trait(Traits.Feature, Traits.Features.CodeActionsInvertConditional)] + public partial class InvertConditionalTests : AbstractCSharpCodeActionTest + { + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsFixAllOccurrences)] + public async Task InvertConditional_FixAllInDocument() + { + await TestInRegularAndScriptAsync( +@"class C +{ + void M(bool x, int a, int b) + { + var c = {|FixAllInDocument:|}x ? a : b; + } + + void M2(bool x, int a, int b) + { + var c = x ? a : b; + } +}", +@"class C +{ + void M(bool x, int a, int b) + { + var c = !x ? b : a; + } + + void M2(bool x, int a, int b) + { + var c = !x ? b : a; + } +}"); + } + + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsFixAllOccurrences)] + public async Task InvertConditional_FixAllInProject() + { + await TestInRegularAndScriptAsync( +@" + + + +class Program1 +{ + void M1(bool x, int a, int b) + { + var c = {|FixAllInProject:|}x ? a : b; + } +} + + +class Program2 +{ + void M2(bool x, int a, int b) + { + var c = x ? a : b; + } +} + + + + +class Program3 +{ + void M3(bool x, int a, int b) + { + var c = x ? a : b; + } +} + + +", +@" + + +class Program1 +{ + void M1(bool x, int a, int b) + { + var c = !x ? b : a; + } +} + + +class Program2 +{ + void M2(bool x, int a, int b) + { + var c = !x ? b : a; + } +} + + + + +class Program3 +{ + void M3(bool x, int a, int b) + { + var c = x ? a : b; + } +} + + +"); + } + + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsFixAllOccurrences)] + public async Task InvertConditional_FixAllInSolution() + { + await TestInRegularAndScriptAsync( +@" + + + +class Program1 +{ + void M1(bool x, int a, int b) + { + var c = {|FixAllInSolution:|}x ? a : b; + } +} + + +class Program2 +{ + void M2(bool x, int a, int b) + { + var c = x ? a : b; + } +} + + + + +class Program3 +{ + void M3(bool x, int a, int b) + { + var c = x ? a : b; + } +} + + +", +@" + + +class Program1 +{ + void M1(bool x, int a, int b) + { + var c = !x ? b : a; + } +} + + +class Program2 +{ + void M2(bool x, int a, int b) + { + var c = !x ? b : a; + } +} + + + + +class Program3 +{ + void M3(bool x, int a, int b) + { + var c = !x ? b : a; + } +} + + +"); + } + + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsFixAllOccurrences)] + public async Task InvertConditional_FixAllInContainingMember() + { + await TestInRegularAndScriptAsync( +@"class C +{ + void M(bool x, int a, int b) + { + var c = {|FixAllInContainingMember:|}x ? a : b; + var c2 = x ? a : b; + } + + void M2(bool x, int a, int b) + { + var c = x ? a : b; + var c2 = x ? a : b; + } +} + +class C2 +{ + void M(bool x, int a, int b) + { + var c = x ? a : b; + var c2 = x ? a : b; + } +}", +@"class C +{ + void M(bool x, int a, int b) + { + var c = !x ? b : a; + var c2 = !x ? b : a; + } + + void M2(bool x, int a, int b) + { + var c = x ? a : b; + var c2 = x ? a : b; + } +} + +class C2 +{ + void M(bool x, int a, int b) + { + var c = x ? a : b; + var c2 = x ? a : b; + } +}"); + } + + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsFixAllOccurrences)] + public async Task InvertConditional_FixAllInContainingType() + { + await TestInRegularAndScriptAsync( +@"partial class C +{ + void M(bool x, int a, int b) + { + var c = {|FixAllInContainingType:|}x ? a : b; + var c2 = x ? a : b; + } + + void M2(bool x, int a, int b) + { + var c = x ? a : b; + var c2 = x ? a : b; + } +} + +class C2 +{ + void M(bool x, int a, int b) + { + var c = x ? a : b; + var c2 = x ? a : b; + } +} + +partial class C +{ + void M3(bool x, int a, int b) + { + var c = x ? a : b; + var c2 = x ? a : b; + } +}", +@"partial class C +{ + void M(bool x, int a, int b) + { + var c = !x ? b : a; + var c2 = !x ? b : a; + } + + void M2(bool x, int a, int b) + { + var c = !x ? b : a; + var c2 = !x ? b : a; + } +} + +class C2 +{ + void M(bool x, int a, int b) + { + var c = x ? a : b; + var c2 = x ? a : b; + } +} + +partial class C +{ + void M3(bool x, int a, int b) + { + var c = !x ? b : a; + var c2 = !x ? b : a; + } +}"); + } + + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsFixAllOccurrences)] + public async Task InvertConditional_FixAllInContainingType_AcrossFiles() + { + await TestInRegularAndScriptAsync( +@" + + + +partial class Program1 +{ + void M1(bool x, int a, int b) + { + var c = {|FixAllInContainingType:|}x ? a : b; + } + + void M2(bool x, int a, int b) + { + var c = x ? a : b; + } +} + + +partial class Program1 +{ + void M3(bool x, int a, int b) + { + var c = x ? a : b; + } +} + +class Program2 +{ + void M2(bool x, int a, int b) + { + var c = x ? a : b; + } +} + + + + +class Program3 +{ + void M3(bool x, int a, int b) + { + var c = x ? a : b; + } +} + + +", +@" + + +partial class Program1 +{ + void M1(bool x, int a, int b) + { + var c = !x ? b : a; + } + + void M2(bool x, int a, int b) + { + var c = !x ? b : a; + } +} + + +partial class Program1 +{ + void M3(bool x, int a, int b) + { + var c = !x ? b : a; + } +} + +class Program2 +{ + void M2(bool x, int a, int b) + { + var c = x ? a : b; + } +} + + + + +class Program3 +{ + void M3(bool x, int a, int b) + { + var c = x ? a : b; + } +} + + +"); + } + } +} diff --git a/src/EditorFeatures/DiagnosticsTestUtilities/CodeActions/AbstractCodeActionOrUserDiagnosticTest.cs b/src/EditorFeatures/DiagnosticsTestUtilities/CodeActions/AbstractCodeActionOrUserDiagnosticTest.cs index 31d9a9bdefae0..1d23ea48c81be 100644 --- a/src/EditorFeatures/DiagnosticsTestUtilities/CodeActions/AbstractCodeActionOrUserDiagnosticTest.cs +++ b/src/EditorFeatures/DiagnosticsTestUtilities/CodeActions/AbstractCodeActionOrUserDiagnosticTest.cs @@ -894,5 +894,91 @@ protected async Task TestAllInRegularAndScriptAsync( await TestActionCountAsync(input, outputs.Length, parameters); } + + protected static void GetDocumentAndSelectSpanOrAnnotatedSpan( + TestWorkspace workspace, + out Document document, + out TextSpan span, + out string annotation) + { + annotation = null; + if (!TryGetDocumentAndSelectSpan(workspace, out document, out span)) + { + document = GetDocumentAndAnnotatedSpan(workspace, out annotation, out span); + } + } + + private static bool TryGetDocumentAndSelectSpan(TestWorkspace workspace, out Document document, out TextSpan span) + { + var hostDocument = workspace.Documents.FirstOrDefault(d => d.SelectedSpans.Any()); + if (hostDocument == null) + { + // If there wasn't a span, see if there was a $$ caret. we'll create an empty span + // there if so. + hostDocument = workspace.Documents.FirstOrDefault(d => d.CursorPosition != null); + if (hostDocument == null) + { + document = null; + span = default; + return false; + } + + span = new TextSpan(hostDocument.CursorPosition.Value, 0); + document = workspace.CurrentSolution.GetDocument(hostDocument.Id); + return true; + } + + span = hostDocument.SelectedSpans.Single(); + document = workspace.CurrentSolution.GetDocument(hostDocument.Id); + return true; + } + + private static Document GetDocumentAndAnnotatedSpan(TestWorkspace workspace, out string annotation, out TextSpan span) + { + var annotatedDocuments = workspace.Documents.Where(d => d.AnnotatedSpans.Any()); + var hostDocument = annotatedDocuments.Single(); + var annotatedSpan = hostDocument.AnnotatedSpans.Single(); + annotation = annotatedSpan.Key; + span = annotatedSpan.Value.Single(); + return workspace.CurrentSolution.GetDocument(hostDocument.Id); + } + + protected static FixAllScope? GetFixAllScopeForCodeFix(string annotation) + { + if (annotation == null) + { + return null; + } + + return annotation switch + { + "FixAllInDocument" => FixAllScope.Document, + "FixAllInProject" => FixAllScope.Project, + "FixAllInSolution" => FixAllScope.Solution, + "FixAllInContainingMember" => FixAllScope.ContainingMember, + "FixAllInContainingType" => FixAllScope.ContainingType, + "FixAllInSelection" => FixAllScope.Custom, + _ => throw new InvalidProgramException("Incorrect FixAll annotation in test"), + }; + } + + private protected static CodeRefactorings.FixAllScope? GetFixAllScopeForCodeRefactoring(string annotation) + { + if (annotation == null) + { + return null; + } + + return annotation switch + { + "FixAllInDocument" => CodeRefactorings.FixAllScope.Document, + "FixAllInProject" => CodeRefactorings.FixAllScope.Project, + "FixAllInSolution" => CodeRefactorings.FixAllScope.Solution, + "FixAllInContainingMember" => CodeRefactorings.FixAllScope.ContainingMember, + "FixAllInContainingType" => CodeRefactorings.FixAllScope.ContainingType, + "FixAllInSelection" => CodeRefactorings.FixAllScope.Custom, + _ => throw new InvalidProgramException("Incorrect FixAll annotation in test"), + }; + } } } diff --git a/src/EditorFeatures/DiagnosticsTestUtilities/CodeActions/AbstractCodeActionTest.cs b/src/EditorFeatures/DiagnosticsTestUtilities/CodeActions/AbstractCodeActionTest.cs index 26159883e661d..065791d3dd712 100644 --- a/src/EditorFeatures/DiagnosticsTestUtilities/CodeActions/AbstractCodeActionTest.cs +++ b/src/EditorFeatures/DiagnosticsTestUtilities/CodeActions/AbstractCodeActionTest.cs @@ -5,10 +5,8 @@ #nullable disable using System; -using System.Collections.Generic; using System.Collections.Immutable; using System.Composition; -using System.Diagnostics; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -20,6 +18,7 @@ using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.PickMembers; using Microsoft.CodeAnalysis.PooledObjects; +using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Shared.Utilities; using Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; @@ -35,12 +34,49 @@ protected abstract CodeRefactoringProvider CreateCodeRefactoringProvider( protected override async Task<(ImmutableArray, CodeAction actionToInvoke)> GetCodeActionsAsync( TestWorkspace workspace, TestParameters parameters) { + GetDocumentAndSelectSpanOrAnnotatedSpan(workspace, out var document, out var span, out var annotation); + var refactoring = await GetCodeRefactoringAsync(workspace, parameters); var actions = refactoring == null ? ImmutableArray.Empty : refactoring.CodeActions.Select(n => n.action).AsImmutable(); actions = MassageActions(actions); - return (actions, actions.IsDefaultOrEmpty ? null : actions[parameters.index]); + + var fixAllScope = GetFixAllScopeForCodeRefactoring(annotation); + + if (fixAllScope is FixAllScope.ContainingMember or FixAllScope.ContainingType && + document.GetLanguageService() is FixAll.IFixAllSpanMappingService spanMappingService) + { + var documentsAndSpansToFix = await spanMappingService.GetFixAllSpansAsync( + document, span, fixAllScope.Value, CancellationToken.None).ConfigureAwait(false); + if (documentsAndSpansToFix.IsEmpty) + { + return (ImmutableArray.Empty, null); + } + } + + var actionToInvoke = actions.IsDefaultOrEmpty ? null : actions[parameters.index]; + if (actionToInvoke == null || fixAllScope == null) + return (actions, actionToInvoke); + + var fixAllCodeAction = await GetFixAllFixAsync(actionToInvoke, + refactoring.Provider, document, span, fixAllScope.Value).ConfigureAwait(false); + return (ImmutableArray.Create(fixAllCodeAction), fixAllCodeAction); + } + + private static async Task GetFixAllFixAsync( + CodeAction originalCodeAction, + CodeRefactoringProvider provider, + Document document, + TextSpan selectionSpan, + FixAllScope scope) + { + var fixAllProvider = provider.GetFixAllProvider(); + Assert.NotNull(fixAllProvider); + + var fixAllState = new FixAllState(fixAllProvider, document, selectionSpan, provider, scope, originalCodeAction); + var fixAllContext = new FixAllContext(fixAllState, new ProgressTracker(), CancellationToken.None); + return await fixAllProvider.GetFixAsync(fixAllContext).ConfigureAwait(false); } protected override Task> GetDiagnosticsWorkerAsync(TestWorkspace workspace, TestParameters parameters) @@ -48,17 +84,22 @@ protected override Task> GetDiagnosticsWorkerAsync(Te internal async Task GetCodeRefactoringAsync( TestWorkspace workspace, TestParameters parameters) + { + GetDocumentAndSelectSpanOrAnnotatedSpan(workspace, out var document, out var span, out _); + return await GetCodeRefactoringAsync(document, span, workspace, parameters).ConfigureAwait(false); + } + + internal async Task GetCodeRefactoringAsync( + Document document, + TextSpan selectedOrAnnotatedSpan, + TestWorkspace workspace, + TestParameters parameters) { var provider = CreateCodeRefactoringProvider(workspace, parameters); - var documentsWithSelections = workspace.Documents.Where(d => !d.IsLinkFile && d.SelectedSpans.Count == 1); - Debug.Assert(documentsWithSelections.Count() == 1, "One document must have a single span annotation"); - var span = documentsWithSelections.Single().SelectedSpans.Single(); var actions = ArrayBuilder<(CodeAction, TextSpan?)>.GetInstance(); - var document = workspace.CurrentSolution.GetDocument(documentsWithSelections.Single().Id); - var context = new CodeRefactoringContext(document, span, (a, t) => actions.Add((a, t)), parameters.codeActionOptions, CancellationToken.None); + var context = new CodeRefactoringContext(document, selectedOrAnnotatedSpan, (a, t) => actions.Add((a, t)), parameters.codeActionOptions, CancellationToken.None); await provider.ComputeRefactoringsAsync(context); - var result = actions.Count > 0 ? new CodeRefactoring(provider, actions.ToImmutable(), FixAllProviderInfo.Create(provider)) : null; actions.Free(); return result; diff --git a/src/EditorFeatures/DiagnosticsTestUtilities/Diagnostics/AbstractDiagnosticProviderBasedUserDiagnosticTest.cs b/src/EditorFeatures/DiagnosticsTestUtilities/Diagnostics/AbstractDiagnosticProviderBasedUserDiagnosticTest.cs index ecfb91176158f..7eb4fd4b2dd3c 100644 --- a/src/EditorFeatures/DiagnosticsTestUtilities/Diagnostics/AbstractDiagnosticProviderBasedUserDiagnosticTest.cs +++ b/src/EditorFeatures/DiagnosticsTestUtilities/Diagnostics/AbstractDiagnosticProviderBasedUserDiagnosticTest.cs @@ -158,11 +158,7 @@ internal override async Task> GetDiagnosticsAsync( var (analyzer, fixer) = GetOrCreateDiagnosticProviderAndFixer(workspace, parameters); AddAnalyzerToWorkspace(workspace, analyzer, parameters); - string annotation = null; - if (!TryGetDocumentAndSelectSpan(workspace, out var document, out var span)) - { - document = GetDocumentAndAnnotatedSpan(workspace, out annotation, out span); - } + GetDocumentAndSelectSpanOrAnnotatedSpan(workspace, out var document, out var span, out var annotation); var testDriver = new TestDiagnosticAnalyzerDriver(workspace, document.Project); var filterSpan = parameters.includeDiagnosticsOutsideSelection ? (TextSpan?)null : span; diff --git a/src/EditorFeatures/DiagnosticsTestUtilities/Diagnostics/AbstractSuppressionDiagnosticTest.cs b/src/EditorFeatures/DiagnosticsTestUtilities/Diagnostics/AbstractSuppressionDiagnosticTest.cs index 153893ffa064d..be3f0ea3be652 100644 --- a/src/EditorFeatures/DiagnosticsTestUtilities/Diagnostics/AbstractSuppressionDiagnosticTest.cs +++ b/src/EditorFeatures/DiagnosticsTestUtilities/Diagnostics/AbstractSuppressionDiagnosticTest.cs @@ -83,11 +83,7 @@ internal override async Task> GetDiagnosticsAsync( var (analyzer, fixer) = CreateDiagnosticProviderAndFixer(workspace); AddAnalyzerToWorkspace(workspace, analyzer, parameters); - string annotation = null; - if (!TryGetDocumentAndSelectSpan(workspace, out var document, out var span)) - { - document = GetDocumentAndAnnotatedSpan(workspace, out annotation, out span); - } + GetDocumentAndSelectSpanOrAnnotatedSpan(workspace, out var document, out var span, out var annotation); var testDriver = new TestDiagnosticAnalyzerDriver(workspace, document.Project, includeSuppressedDiagnostics: IncludeSuppressedDiagnostics); var diagnostics = (await testDriver.GetAllDiagnosticsAsync(document, span)) diff --git a/src/EditorFeatures/DiagnosticsTestUtilities/Diagnostics/AbstractUnncessarySuppressionDiagnosticTest.cs b/src/EditorFeatures/DiagnosticsTestUtilities/Diagnostics/AbstractUnncessarySuppressionDiagnosticTest.cs index dde2727fe3d0c..f77a5b19feac9 100644 --- a/src/EditorFeatures/DiagnosticsTestUtilities/Diagnostics/AbstractUnncessarySuppressionDiagnosticTest.cs +++ b/src/EditorFeatures/DiagnosticsTestUtilities/Diagnostics/AbstractUnncessarySuppressionDiagnosticTest.cs @@ -48,11 +48,7 @@ internal override async Task> GetDiagnosticsAsync( { AddAnalyzersToWorkspace(workspace); - string annotation = null; - if (!TryGetDocumentAndSelectSpan(workspace, out var document, out var span)) - { - document = GetDocumentAndAnnotatedSpan(workspace, out annotation, out span); - } + GetDocumentAndSelectSpanOrAnnotatedSpan(workspace, out var document, out var span, out var annotation); // Include suppressed diagnostics as they are needed by unnecessary suppressions analyzer. var testDriver = new TestDiagnosticAnalyzerDriver(workspace, document.Project, includeSuppressedDiagnostics: true); diff --git a/src/EditorFeatures/DiagnosticsTestUtilities/Diagnostics/AbstractUserDiagnosticTest.cs b/src/EditorFeatures/DiagnosticsTestUtilities/Diagnostics/AbstractUserDiagnosticTest.cs index 64e35dc16b639..2edef5b7057d2 100644 --- a/src/EditorFeatures/DiagnosticsTestUtilities/Diagnostics/AbstractUserDiagnosticTest.cs +++ b/src/EditorFeatures/DiagnosticsTestUtilities/Diagnostics/AbstractUserDiagnosticTest.cs @@ -116,61 +116,6 @@ protected static Document GetDocumentAndSelectSpan(TestWorkspace workspace, out return workspace.CurrentSolution.GetDocument(hostDocument.Id); } - protected static bool TryGetDocumentAndSelectSpan(TestWorkspace workspace, out Document document, out TextSpan span) - { - var hostDocument = workspace.Documents.FirstOrDefault(d => d.SelectedSpans.Any()); - if (hostDocument == null) - { - // If there wasn't a span, see if there was a $$ caret. we'll create an empty span - // there if so. - hostDocument = workspace.Documents.FirstOrDefault(d => d.CursorPosition != null); - if (hostDocument == null) - { - document = null; - span = default; - return false; - } - - span = new TextSpan(hostDocument.CursorPosition.Value, 0); - document = workspace.CurrentSolution.GetDocument(hostDocument.Id); - return true; - } - - span = hostDocument.SelectedSpans.Single(); - document = workspace.CurrentSolution.GetDocument(hostDocument.Id); - return true; - } - - protected static Document GetDocumentAndAnnotatedSpan(TestWorkspace workspace, out string annotation, out TextSpan span) - { - var annotatedDocuments = workspace.Documents.Where(d => d.AnnotatedSpans.Any()); - Debug.Assert(!annotatedDocuments.IsEmpty(), "No annotated span found"); - var hostDocument = annotatedDocuments.Single(); - var annotatedSpan = hostDocument.AnnotatedSpans.Single(); - annotation = annotatedSpan.Key; - span = annotatedSpan.Value.Single(); - return workspace.CurrentSolution.GetDocument(hostDocument.Id); - } - - protected static FixAllScope? GetFixAllScope(string annotation) - { - if (annotation == null) - { - return null; - } - - return annotation switch - { - "FixAllInDocument" => FixAllScope.Document, - "FixAllInProject" => FixAllScope.Project, - "FixAllInSolution" => FixAllScope.Solution, - "FixAllInContainingMember" => FixAllScope.ContainingMember, - "FixAllInContainingType" => FixAllScope.ContainingType, - "FixAllInSelection" => FixAllScope.Custom, - _ => throw new InvalidProgramException("Incorrect FixAll annotation in test"), - }; - } - internal async Task<(ImmutableArray, ImmutableArray, CodeAction actionToInvoke)> GetDiagnosticAndFixesAsync( IEnumerable diagnostics, CodeFixProvider fixer, @@ -186,7 +131,7 @@ protected static Document GetDocumentAndAnnotatedSpan(TestWorkspace workspace, o return (ImmutableArray.Empty, ImmutableArray.Empty, null); } - var scope = GetFixAllScope(annotation); + var scope = GetFixAllScopeForCodeFix(annotation); if (scope is FixAllScope.ContainingMember or FixAllScope.ContainingType && document.GetLanguageService() is IFixAllSpanMappingService spanMappingService) diff --git a/src/EditorFeatures/VisualBasicTest/InvertConditional/InvertIfTests_FixAll.vb b/src/EditorFeatures/VisualBasicTest/InvertConditional/InvertIfTests_FixAll.vb new file mode 100644 index 0000000000000..f54807cc440d9 --- /dev/null +++ b/src/EditorFeatures/VisualBasicTest/InvertConditional/InvertIfTests_FixAll.vb @@ -0,0 +1,368 @@ +' 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. + +Imports Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.CodeRefactorings + +Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.InvertConditional + + Partial Public Class InvertConditionalTests + Inherits AbstractVisualBasicCodeActionTest + + + Public Async Function InvertConditional_FixAllInDocument() As Task + Await TestInRegularAndScriptAsync( +"Class C + Sub M1(x As Boolean, a As Integer, b As Integer) + Dim c = {|FixAllInDocument:|}If(x, a, b) + End Sub + + Sub M2(x As Boolean, a As Integer, b As Integer) + Dim c = If(x, a, b) + End Sub +End Class + +Class C2 + Sub M3(x As Boolean, a As Integer, b As Integer) + Dim c = If(x, a, b) + End Sub +End Class", +"Class C + Sub M1(x As Boolean, a As Integer, b As Integer) + Dim c = If(Not x, b, a) + End Sub + + Sub M2(x As Boolean, a As Integer, b As Integer) + Dim c = If(Not x, b, a) + End Sub +End Class + +Class C2 + Sub M3(x As Boolean, a As Integer, b As Integer) + Dim c = If(Not x, b, a) + End Sub +End Class") + End Function + + + Public Async Function InvertConditional_FixAllInProject() As Task + Dim input = + + + + + + + + Assembly1 + + + + .ToString() + + Dim expected = + + + + + + + + Assembly1 + + + + .ToString() + + Await TestInRegularAndScriptAsync(input, expected) + End Function + + + Public Async Function InvertConditional_FixAllInSolution() As Task + Dim input = + + + + + + + + Assembly1 + + + + .ToString() + + Dim expected = + + + + + + + + Assembly1 + + + + .ToString() + + Await TestInRegularAndScriptAsync(input, expected) + End Function + + + Public Async Function InvertConditional_FixAllInContainingMember() As Task + Await TestInRegularAndScriptAsync( +"Class C + Sub M1(x As Boolean, a As Integer, b As Integer) + Dim c = {|FixAllInContainingMember:|}If(x, a, b) + Dim c2 = If(x, a, b) + End Sub + + Sub M2(x As Boolean, a As Integer, b As Integer) + Dim c = If(x, a, b) + Dim c2 = If(x, a, b) + End Sub +End Class + +Class C2 + Sub M3(x As Boolean, a As Integer, b As Integer) + Dim c = If(x, a, b) + Dim c2 = If(x, a, b) + End Sub +End Class", +"Class C + Sub M1(x As Boolean, a As Integer, b As Integer) + Dim c = If(Not x, b, a) + Dim c2 = If(Not x, b, a) + End Sub + + Sub M2(x As Boolean, a As Integer, b As Integer) + Dim c = If(x, a, b) + Dim c2 = If(x, a, b) + End Sub +End Class + +Class C2 + Sub M3(x As Boolean, a As Integer, b As Integer) + Dim c = If(x, a, b) + Dim c2 = If(x, a, b) + End Sub +End Class") + End Function + + + Public Async Function InvertConditional_FixAllInContainingType() As Task + Await TestInRegularAndScriptAsync( +"Partial Class C + Sub M1(x As Boolean, a As Integer, b As Integer) + Dim c = {|FixAllInContainingType:|}If(x, a, b) + Dim c2 = If(x, a, b) + End Sub + + Sub M2(x As Boolean, a As Integer, b As Integer) + Dim c = If(x, a, b) + Dim c2 = If(x, a, b) + End Sub +End Class + +Class C2 + Sub M3(x As Boolean, a As Integer, b As Integer) + Dim c = If(x, a, b) + Dim c2 = If(x, a, b) + End Sub +End Class + +Partial Class C + Sub M3(x As Boolean, a As Integer, b As Integer) + Dim c = If(x, a, b) + Dim c2 = If(x, a, b) + End Sub +End Class", +"Partial Class C + Sub M1(x As Boolean, a As Integer, b As Integer) + Dim c = If(Not x, b, a) + Dim c2 = If(Not x, b, a) + End Sub + + Sub M2(x As Boolean, a As Integer, b As Integer) + Dim c = If(Not x, b, a) + Dim c2 = If(Not x, b, a) + End Sub +End Class + +Class C2 + Sub M3(x As Boolean, a As Integer, b As Integer) + Dim c = If(x, a, b) + Dim c2 = If(x, a, b) + End Sub +End Class + +Partial Class C + Sub M3(x As Boolean, a As Integer, b As Integer) + Dim c = If(Not x, b, a) + Dim c2 = If(Not x, b, a) + End Sub +End Class") + End Function + + + Public Async Function InvertConditional_FixAllInContainingType_AcrossFiles() As Task + Dim input = + + + + + + + + Assembly1 + + + + .ToString() + + Dim expected = + + + + + + + + Assembly1 + + + + .ToString() + + Await TestInRegularAndScriptAsync(input, expected) + End Function + End Class +End Namespace diff --git a/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpCodeActions.cs b/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpCodeActions.cs index 9873aaa72ea90..e3c2f57d3252a 100644 --- a/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpCodeActions.cs +++ b/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpCodeActions.cs @@ -910,7 +910,7 @@ static async Task VerifyDiagnosticInErrorListAsync(string expectedSeverity, Test [IdeFact] [Trait(Traits.Feature, Traits.Features.CodeActionsFixAllOccurrences)] - public async Task TestFixAllOccurrences_ContainingMember() + public async Task TestFixAllOccurrences_CodeFix_ContainingMember() { var markup = @" class Program1 @@ -998,7 +998,7 @@ await TestServices.EditorVerifier.CodeActionAsync( [IdeFact] [Trait(Traits.Feature, Traits.Features.CodeActionsFixAllOccurrences)] - public async Task TestFixAllOccurrences_ContainingType() + public async Task TestFixAllOccurrences_CodeFix_ContainingType() { var markup1 = @" partial class Program1 @@ -1163,5 +1163,215 @@ await TestServices.EditorVerifier.CodeActionAsync( AssertEx.EqualOrDiff(expectedText2, await TestServices.SolutionExplorer.GetFileContentsAsync(ProjectName, "Class2.cs", HangMitigatingCancellationToken)); } + + [IdeFact] + [Trait(Traits.Feature, Traits.Features.CodeActionsFixAllOccurrences)] + public async Task TestFixAllOccurrences_CodeRefactoring_ContainingMember() + { + var markup = @" +class C1 +{ + void M(bool x, int a, int b) + { + var c = $$x ? a : b; + var c2 = x ? a : b; + } + + void M2(bool x, int a, int b) + { + var c = x ? a : b; + } +} + +class C2 +{ + void M3(bool x, int a, int b) + { + var c = x ? a : b; + } +}"; + var expectedText = @" +class C1 +{ + void M(bool x, int a, int b) + { + var c = !x ? b : a; + var c2 = !x ? b : a; + } + + void M2(bool x, int a, int b) + { + var c = x ? a : b; + } +} + +class C2 +{ + void M3(bool x, int a, int b) + { + var c = x ? a : b; + } +}"; + + await TestServices.SolutionExplorer.OpenFileAsync(ProjectName, "Class1.cs", HangMitigatingCancellationToken); + + MarkupTestFile.GetSpans(markup, out _, out ImmutableArray _); + await SetUpEditorAsync(markup, HangMitigatingCancellationToken); + await TestServices.Workspace.WaitForAllAsyncOperationsAsync( + new[] + { + FeatureAttribute.Workspace, + FeatureAttribute.SolutionCrawler, + FeatureAttribute.DiagnosticService, + FeatureAttribute.ErrorSquiggles + }, + HangMitigatingCancellationToken); + + await TestServices.Editor.InvokeCodeActionListAsync(HangMitigatingCancellationToken); + + await TestServices.EditorVerifier.CodeActionAsync( + "Invert conditional", + applyFix: true, + fixAllScope: FixAllScope.ContainingMember, + cancellationToken: HangMitigatingCancellationToken); + + AssertEx.EqualOrDiff(expectedText, await TestServices.Editor.GetTextAsync(HangMitigatingCancellationToken)); + } + + [IdeFact] + [Trait(Traits.Feature, Traits.Features.CodeActionsFixAllOccurrences)] + public async Task TestFixAllOccurrences_CodeRefactoring_ContainingType() + { + var markup1 = @" +partial class C1 +{ + void M(bool x, int a, int b) + { + var c = $$x ? a : b; + var c2 = x ? a : b; + } + + void M2(bool x, int a, int b) + { + var c = x ? a : b; + var c2 = x ? a : b; + } +} + +class C2 +{ + void M3(bool x, int a, int b) + { + var c = x ? a : b; + var c2 = x ? a : b; + } +} + +partial class C1 +{ + void M4(bool x, int a, int b) + { + var c = x ? a : b; + var c2 = x ? a : b; + } +}"; + var expectedText1 = @" +partial class C1 +{ + void M(bool x, int a, int b) + { + var c = !x ? b : a; + var c2 = !x ? b : a; + } + + void M2(bool x, int a, int b) + { + var c = !x ? b : a; + var c2 = !x ? b : a; + } +} + +class C2 +{ + void M3(bool x, int a, int b) + { + var c = x ? a : b; + var c2 = x ? a : b; + } +} + +partial class C1 +{ + void M4(bool x, int a, int b) + { + var c = !x ? b : a; + var c2 = !x ? b : a; + } +}"; + + var markup2 = @" +partial class C1 +{ + void M5(bool x, int a, int b) + { + var c = x ? a : b; + var c2 = x ? a : b; + } +} + +class C2 +{ + void M6(bool x, int a, int b) + { + var c = x ? a : b; + var c2 = x ? a : b; + } +}"; + var expectedText2 = @" +partial class C1 +{ + void M5(bool x, int a, int b) + { + var c = !x ? b : a; + var c2 = !x ? b : a; + } +} + +class C2 +{ + void M6(bool x, int a, int b) + { + var c = x ? a : b; + var c2 = x ? a : b; + } +}"; + + await TestServices.SolutionExplorer.AddFileAsync(ProjectName, "Class2.cs", markup2, cancellationToken: HangMitigatingCancellationToken); + await TestServices.SolutionExplorer.OpenFileAsync(ProjectName, "Class1.cs", HangMitigatingCancellationToken); + + MarkupTestFile.GetSpans(markup1, out _, out ImmutableArray _); + await SetUpEditorAsync(markup1, HangMitigatingCancellationToken); + await TestServices.Workspace.WaitForAllAsyncOperationsAsync( + new[] + { + FeatureAttribute.Workspace, + FeatureAttribute.SolutionCrawler, + FeatureAttribute.DiagnosticService, + FeatureAttribute.ErrorSquiggles + }, + HangMitigatingCancellationToken); + + await TestServices.Editor.InvokeCodeActionListAsync(HangMitigatingCancellationToken); + + await TestServices.EditorVerifier.CodeActionAsync( + "Invert conditional", + applyFix: true, + fixAllScope: FixAllScope.ContainingType, + cancellationToken: HangMitigatingCancellationToken); + + AssertEx.EqualOrDiff(expectedText1, await TestServices.Editor.GetTextAsync(HangMitigatingCancellationToken)); + + AssertEx.EqualOrDiff(expectedText2, await TestServices.SolutionExplorer.GetFileContentsAsync(ProjectName, "Class2.cs", HangMitigatingCancellationToken)); + } } } From 4f20ef8340aac4266d7b79b0509d65b0524d196d Mon Sep 17 00:00:00 2001 From: Manish Vasani Date: Thu, 14 Apr 2022 02:24:20 -0700 Subject: [PATCH 16/18] Add integration test support for FixAll for code refactorings and fix up the added fix all integration tests --- .../FixAllCodeRefactoringGetFixesService.cs | 4 +- .../FixAllCodeRefactoringCodeAction.cs | 20 +++- .../IFixAllCodeRefactoringGetFixesService.cs | 2 +- .../CSharp/CSharpCodeActions.cs | 30 +++--- .../InProcess/EditorInProcess.cs | 100 +++++++++++++++--- .../InProcess/EditorVerifierInProcess.cs | 20 ++-- 6 files changed, 136 insertions(+), 40 deletions(-) diff --git a/src/EditorFeatures/Core.Wpf/Suggestions/FixAll/FixAllCodeRefactoringGetFixesService.cs b/src/EditorFeatures/Core.Wpf/Suggestions/FixAll/FixAllCodeRefactoringGetFixesService.cs index c1843dd3cac13..31e27d9c2c1e0 100644 --- a/src/EditorFeatures/Core.Wpf/Suggestions/FixAll/FixAllCodeRefactoringGetFixesService.cs +++ b/src/EditorFeatures/Core.Wpf/Suggestions/FixAll/FixAllCodeRefactoringGetFixesService.cs @@ -38,7 +38,7 @@ public IWorkspaceService CreateService(HostWorkspaceServices workspaceServices) return await codeAction.GetChangedSolutionInternalAsync(cancellationToken: fixAllContext.CancellationToken).ConfigureAwait(false); } - public async Task> GetFixAllOperationsAsync(FixAllContext fixAllContext) + public async Task> GetFixAllOperationsAsync(FixAllContext fixAllContext, bool showPreviewChangesDialog) { var codeAction = await GetFixAllCodeActionAsync(fixAllContext).ConfigureAwait(false); if (codeAction == null) @@ -49,7 +49,7 @@ public async Task> GetFixAllOperationsAsync( return await FixAllGetFixesServiceHelper.GetFixAllOperationsAsync( codeAction, fixAllContext.State.Project, fixAllContext.State.CorrelationId, FunctionId.Refactoring_FixAllOccurrencesPreviewChanges, - showPreviewChangesDialog: true, fixAllContext.CancellationToken).ConfigureAwait(false); + showPreviewChangesDialog, fixAllContext.CancellationToken).ConfigureAwait(false); } private static async Task GetFixAllCodeActionAsync(FixAllContext fixAllContext) diff --git a/src/Features/Core/Portable/CodeRefactorings/FixAllOccurences/FixAllCodeRefactoringCodeAction.cs b/src/Features/Core/Portable/CodeRefactorings/FixAllOccurences/FixAllCodeRefactoringCodeAction.cs index aa40ff6ee57eb..d47277e1e0d06 100644 --- a/src/Features/Core/Portable/CodeRefactorings/FixAllOccurences/FixAllCodeRefactoringCodeAction.cs +++ b/src/Features/Core/Portable/CodeRefactorings/FixAllOccurences/FixAllCodeRefactoringCodeAction.cs @@ -15,6 +15,7 @@ namespace Microsoft.CodeAnalysis.CodeRefactorings internal sealed class FixAllCodeRefactoringCodeAction : CodeAction { internal readonly FixAllState FixAllState; + private bool _showPreviewChangesDialog; internal FixAllCodeRefactoringCodeAction(FixAllState fixAllState) { @@ -47,7 +48,7 @@ internal override Task> ComputeOperationsAsy var fixAllContext = new FixAllContext(FixAllState, progressTracker, cancellationToken); progressTracker.Description = FixAllContextHelper.GetDefaultFixAllTitle(fixAllContext); - return service.GetFixAllOperationsAsync(fixAllContext); + return service.GetFixAllOperationsAsync(fixAllContext, _showPreviewChangesDialog); } internal sealed override Task GetChangedSolutionAsync( @@ -62,5 +63,22 @@ internal override Task> ComputeOperationsAsy return service.GetFixAllChangedSolutionAsync(fixAllContext); } + + internal TestAccessor GetTestAccessor() + => new(this); + + internal readonly struct TestAccessor + { + private readonly FixAllCodeRefactoringCodeAction _fixAllCodeRefactoringCodeAction; + + internal TestAccessor(FixAllCodeRefactoringCodeAction fixAllCodeRefactoringCodeAction) + => _fixAllCodeRefactoringCodeAction = fixAllCodeRefactoringCodeAction; + + /// + /// Gets a reference to , which can be read or written by test code. + /// + public ref bool ShowPreviewChangesDialog + => ref _fixAllCodeRefactoringCodeAction._showPreviewChangesDialog; + } } } diff --git a/src/Features/Core/Portable/CodeRefactorings/FixAllOccurences/IFixAllCodeRefactoringGetFixesService.cs b/src/Features/Core/Portable/CodeRefactorings/FixAllOccurences/IFixAllCodeRefactoringGetFixesService.cs index f64feb1c56b28..aeedabff53a94 100644 --- a/src/Features/Core/Portable/CodeRefactorings/FixAllOccurences/IFixAllCodeRefactoringGetFixesService.cs +++ b/src/Features/Core/Portable/CodeRefactorings/FixAllOccurences/IFixAllCodeRefactoringGetFixesService.cs @@ -15,7 +15,7 @@ internal interface IFixAllCodeRefactoringGetFixesService : IWorkspaceService /// Computes the fix all occurrences code fix, brings up the preview changes dialog for the fix and /// returns the code action operations corresponding to the fix. /// - Task> GetFixAllOperationsAsync(FixAllContext fixAllContext); + Task> GetFixAllOperationsAsync(FixAllContext fixAllContext, bool showPreviewChangesDialog); /// /// Computes the fix all occurrences code fix and returns the changed solution. diff --git a/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpCodeActions.cs b/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpCodeActions.cs index e3c2f57d3252a..5479334eb88e5 100644 --- a/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpCodeActions.cs +++ b/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpCodeActions.cs @@ -8,7 +8,6 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.Shared.TestHooks; using Microsoft.CodeAnalysis.Test.Utilities; using Microsoft.CodeAnalysis.Text; @@ -18,6 +17,9 @@ using Roslyn.VisualStudio.IntegrationTests; using Xunit; +using CodeFixesFixAllScope = Microsoft.CodeAnalysis.CodeFixes.FixAllScope; +using CodeRefactoringsFixAllScope = Microsoft.CodeAnalysis.CodeRefactorings.FixAllScope; + namespace Roslyn.VisualStudio.NewIntegrationTests.CSharp { public class CSharpCodeActions : AbstractEditorTest @@ -251,7 +253,7 @@ await TestServices.Workspace.WaitForAllAsyncOperationsAsync( await TestServices.EditorVerifier.CodeActionAsync( "Use expression body for properties", applyFix: true, - fixAllScope: FixAllScope.Project, + fixAllScope: CodeFixesFixAllScope.Project, cancellationToken: HangMitigatingCancellationToken); AssertEx.EqualOrDiff(expectedText, await TestServices.Editor.GetTextAsync(HangMitigatingCancellationToken)); @@ -278,7 +280,7 @@ await TestServices.Workspace.WaitForAllAsyncOperationsAsync( await TestServices.EditorVerifier.CodeActionAsync( "Use block body for properties", applyFix: true, - fixAllScope: FixAllScope.Project, + fixAllScope: CodeFixesFixAllScope.Project, cancellationToken: HangMitigatingCancellationToken); expectedText = @" @@ -321,11 +323,11 @@ public int Y2 } [CriticalIdeTheory] - [InlineData(FixAllScope.Project)] - [InlineData(FixAllScope.Solution)] + [InlineData(CodeFixesFixAllScope.Project)] + [InlineData(CodeFixesFixAllScope.Solution)] [Trait(Traits.Feature, Traits.Features.CodeActionsFixAllOccurrences)] [WorkItem(33507, "https://github.com/dotnet/roslyn/issues/33507")] - public async Task FixAllOccurrencesIgnoresGeneratedCode(FixAllScope scope) + public async Task FixAllOccurrencesIgnoresGeneratedCode(CodeFixesFixAllScope scope) { var markup = @" using System; @@ -389,7 +391,7 @@ await TestServices.EditorVerifier.CodeActionAsync( await TestServices.EditorVerifier.CodeActionAsync( "Remove Unnecessary Usings", applyFix: true, - fixAllScope: FixAllScope.Document, + fixAllScope: CodeFixesFixAllScope.Document, cancellationToken: HangMitigatingCancellationToken); AssertEx.EqualOrDiff(generatedSource, await TestServices.Editor.GetTextAsync(HangMitigatingCancellationToken)); @@ -408,11 +410,11 @@ await TestServices.EditorVerifier.CodeActionAsync( } [CriticalIdeTheory] - [InlineData(FixAllScope.Project)] - [InlineData(FixAllScope.Solution)] + [InlineData(CodeFixesFixAllScope.Project)] + [InlineData(CodeFixesFixAllScope.Solution)] [Trait(Traits.Feature, Traits.Features.CodeActionsFixAllOccurrences)] [WorkItem(33507, "https://github.com/dotnet/roslyn/issues/33507")] - public async Task FixAllOccurrencesTriggeredFromGeneratedCode(FixAllScope scope) + public async Task FixAllOccurrencesTriggeredFromGeneratedCode(CodeFixesFixAllScope scope) { var markup = @"// using System; @@ -990,7 +992,7 @@ await TestServices.Workspace.WaitForAllAsyncOperationsAsync( await TestServices.EditorVerifier.CodeActionAsync( "Add braces", applyFix: true, - fixAllScope: FixAllScope.ContainingMember, + fixAllScope: CodeFixesFixAllScope.ContainingMember, cancellationToken: HangMitigatingCancellationToken); AssertEx.EqualOrDiff(expectedText, await TestServices.Editor.GetTextAsync(HangMitigatingCancellationToken)); @@ -1156,7 +1158,7 @@ await TestServices.Workspace.WaitForAllAsyncOperationsAsync( await TestServices.EditorVerifier.CodeActionAsync( "Add braces", applyFix: true, - fixAllScope: FixAllScope.ContainingType, + fixAllScope: CodeFixesFixAllScope.ContainingType, cancellationToken: HangMitigatingCancellationToken); AssertEx.EqualOrDiff(expectedText1, await TestServices.Editor.GetTextAsync(HangMitigatingCancellationToken)); @@ -1232,7 +1234,7 @@ await TestServices.Workspace.WaitForAllAsyncOperationsAsync( await TestServices.EditorVerifier.CodeActionAsync( "Invert conditional", applyFix: true, - fixAllScope: FixAllScope.ContainingMember, + refactoringFixAllScope: CodeRefactoringsFixAllScope.ContainingMember, cancellationToken: HangMitigatingCancellationToken); AssertEx.EqualOrDiff(expectedText, await TestServices.Editor.GetTextAsync(HangMitigatingCancellationToken)); @@ -1366,7 +1368,7 @@ await TestServices.Workspace.WaitForAllAsyncOperationsAsync( await TestServices.EditorVerifier.CodeActionAsync( "Invert conditional", applyFix: true, - fixAllScope: FixAllScope.ContainingType, + refactoringFixAllScope: CodeRefactoringsFixAllScope.ContainingType, cancellationToken: HangMitigatingCancellationToken); AssertEx.EqualOrDiff(expectedText1, await TestServices.Editor.GetTextAsync(HangMitigatingCancellationToken)); diff --git a/src/VisualStudio/IntegrationTest/New.IntegrationTests/InProcess/EditorInProcess.cs b/src/VisualStudio/IntegrationTest/New.IntegrationTests/InProcess/EditorInProcess.cs index 23ef7c7bef84d..28ba39b684130 100644 --- a/src/VisualStudio/IntegrationTest/New.IntegrationTests/InProcess/EditorInProcess.cs +++ b/src/VisualStudio/IntegrationTest/New.IntegrationTests/InProcess/EditorInProcess.cs @@ -5,7 +5,6 @@ using System; using System.Collections.Generic; using System.Collections.Immutable; -using System.Diagnostics; using System.Linq; using System.Runtime.InteropServices; using System.Text; @@ -14,7 +13,6 @@ using System.Windows; using System.Windows.Controls; using System.Windows.Input; -using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.Editor; using Microsoft.CodeAnalysis.Editor.Implementation.Suggestions; using Microsoft.CodeAnalysis.Editor.Shared.Extensions; @@ -43,6 +41,11 @@ using Roslyn.VisualStudio.IntegrationTests; using Roslyn.VisualStudio.IntegrationTests.InProcess; using Xunit; + +using CodeFixesFixAllScope = Microsoft.CodeAnalysis.CodeFixes.FixAllScope; +using CodeRefactoringsFixAllScope = Microsoft.CodeAnalysis.CodeRefactorings.FixAllScope; +using FixAllCodeRefactoringCodeAction = Microsoft.CodeAnalysis.CodeRefactorings.FixAllCodeRefactoringCodeAction; +using FixSomeCodeAction = Microsoft.CodeAnalysis.CodeFixes.FixSomeCodeAction; using IComponentModel = Microsoft.VisualStudio.ComponentModelHost.IComponentModel; using IObjectWithSite = Microsoft.VisualStudio.OLE.Interop.IObjectWithSite; using IOleServiceProvider = Microsoft.VisualStudio.OLE.Interop.IServiceProvider; @@ -672,9 +675,14 @@ public async Task GetLightBulbActionsAsync(CancellationToken cancellat return (await GetLightBulbActionsAsync(broker, view, cancellationToken)).Select(a => a.DisplayText).ToArray(); } - public async Task ApplyLightBulbActionAsync(string actionName, FixAllScope? fixAllScope, bool blockUntilComplete, CancellationToken cancellationToken) + public async Task ApplyLightBulbActionAsync( + string actionName, + CodeFixesFixAllScope? fixAllScope, + CodeRefactoringsFixAllScope? refactoringFixAllScope, + bool blockUntilComplete, + CancellationToken cancellationToken) { - var lightBulbAction = GetLightBulbApplicationAction(actionName, fixAllScope, blockUntilComplete); + var lightBulbAction = GetLightBulbApplicationAction(actionName, fixAllScope, refactoringFixAllScope, blockUntilComplete); var listenerProvider = await GetComponentModelServiceAsync(cancellationToken); var listener = listenerProvider.GetListener(FeatureAttribute.LightBulb); @@ -699,8 +707,14 @@ public async Task ApplyLightBulbActionAsync(string actionName, FixAllScope return true; } - private Func> GetLightBulbApplicationAction(string actionName, FixAllScope? fixAllScope, bool willBlockUntilComplete) + private Func> GetLightBulbApplicationAction( + string actionName, + CodeFixesFixAllScope? fixAllScope, + CodeRefactoringsFixAllScope? refactoringFixAllScope, + bool willBlockUntilComplete) { + Assert.True(fixAllScope == null || refactoringFixAllScope == null); + return async (view, cancellationToken) => { await JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); @@ -723,7 +737,7 @@ private Func> GetLightBulbApplicatio $"ISuggestedAction {actionName} not found. Buffer content type={bufferType}\r\nActions: {sb}"); } - if (fixAllScope != null) + if (fixAllScope != null || refactoringFixAllScope != null) { if (!action.HasActionSets) { @@ -731,22 +745,44 @@ private Func> GetLightBulbApplicatio } var actionSetsForAction = await action.GetActionSetsAsync(cancellationToken); - var fixAllAction = await GetFixAllSuggestedActionAsync(actionSetsForAction, fixAllScope.Value, cancellationToken); - if (fixAllAction == null) + + if (fixAllScope != null) { - throw new InvalidOperationException($"Unable to find FixAll in {fixAllScope} code fix for suggested action '{action.DisplayText}'."); + var fixAllAction = await GetFixAllCodeFixSuggestedActionAsync(actionSetsForAction, fixAllScope.Value, cancellationToken); + if (fixAllAction == null) + { + throw new InvalidOperationException($"Unable to find FixAll in {fixAllScope} code fix for suggested action '{action.DisplayText}'."); + } + + action = fixAllAction; } + else + { + var fixAllAction = await GetFixAllCodeRefactoringSuggestedActionAsync(actionSetsForAction, refactoringFixAllScope!.Value, cancellationToken); + if (fixAllAction == null) + { + throw new InvalidOperationException($"Unable to find FixAll in {refactoringFixAllScope} code fix for suggested action '{action.DisplayText}'."); + } - action = fixAllAction; + action = fixAllAction; + } - if (willBlockUntilComplete - && action is FixAllCodeFixSuggestedAction fixAllSuggestedAction - && fixAllSuggestedAction.CodeAction is FixSomeCodeAction fixSomeCodeAction) + if (willBlockUntilComplete) { // Ensure the preview changes dialog will not be shown. Since the operation 'willBlockUntilComplete', // the caller would not be able to interact with the preview changes dialog, and the tests would // either timeout or deadlock. - fixSomeCodeAction.GetTestAccessor().ShowPreviewChangesDialog = false; + + if (action is FixAllCodeFixSuggestedAction fixAllCodeFixSuggestedAction + && fixAllCodeFixSuggestedAction.CodeAction is FixSomeCodeAction fixSomeCodeAction) + { + fixSomeCodeAction.GetTestAccessor().ShowPreviewChangesDialog = false; + } + else if (action is FixAllCodeRefactoringSuggestedAction fixAllCodeRefactoringSuggestedAction + && fixAllCodeRefactoringSuggestedAction.CodeAction is FixAllCodeRefactoringCodeAction fixAllCodeRefactoringCodeAction) + { + fixAllCodeRefactoringCodeAction.GetTestAccessor().ShowPreviewChangesDialog = false; + } } if (string.IsNullOrEmpty(actionName)) @@ -828,7 +864,7 @@ private async Task> SelectActionsAsync(IEnumerable return actions; } - private async Task GetFixAllSuggestedActionAsync(IEnumerable actionSets, FixAllScope fixAllScope, CancellationToken cancellationToken) + private async Task GetFixAllCodeFixSuggestedActionAsync(IEnumerable actionSets, CodeFixesFixAllScope fixAllScope, CancellationToken cancellationToken) { await JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); @@ -848,7 +884,39 @@ private async Task> SelectActionsAsync(IEnumerable if (action.HasActionSets) { var nestedActionSets = await action.GetActionSetsAsync(cancellationToken); - var fixAllCodeAction = await GetFixAllSuggestedActionAsync(nestedActionSets, fixAllScope, cancellationToken); + var fixAllCodeAction = await GetFixAllCodeFixSuggestedActionAsync(nestedActionSets, fixAllScope, cancellationToken); + if (fixAllCodeAction != null) + { + return fixAllCodeAction; + } + } + } + } + + return null; + } + + private async Task GetFixAllCodeRefactoringSuggestedActionAsync(IEnumerable actionSets, CodeRefactoringsFixAllScope fixAllScope, CancellationToken cancellationToken) + { + await JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); + + foreach (var actionSet in actionSets) + { + foreach (var action in actionSet.Actions) + { + if (action is FixAllCodeRefactoringSuggestedAction fixAllSuggestedAction) + { + var fixAllCodeAction = fixAllSuggestedAction.CodeAction as FixAllCodeRefactoringCodeAction; + if (fixAllCodeAction?.FixAllState?.FixAllScope == fixAllScope) + { + return fixAllSuggestedAction; + } + } + + if (action.HasActionSets) + { + var nestedActionSets = await action.GetActionSetsAsync(cancellationToken); + var fixAllCodeAction = await GetFixAllCodeRefactoringSuggestedActionAsync(nestedActionSets, fixAllScope, cancellationToken); if (fixAllCodeAction != null) { return fixAllCodeAction; diff --git a/src/VisualStudio/IntegrationTest/New.IntegrationTests/InProcess/EditorVerifierInProcess.cs b/src/VisualStudio/IntegrationTest/New.IntegrationTests/InProcess/EditorVerifierInProcess.cs index 1bd04c896c7f4..58ac8dc46ad5a 100644 --- a/src/VisualStudio/IntegrationTest/New.IntegrationTests/InProcess/EditorVerifierInProcess.cs +++ b/src/VisualStudio/IntegrationTest/New.IntegrationTests/InProcess/EditorVerifierInProcess.cs @@ -9,7 +9,6 @@ using System.Threading.Tasks; using Microsoft; using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.Internal.Log; using Microsoft.CodeAnalysis.Shared.TestHooks; using Microsoft.VisualStudio.Extensibility.Testing; @@ -19,6 +18,9 @@ using Roslyn.Utilities; using Xunit; +using CodeFixesFixAllScope = Microsoft.CodeAnalysis.CodeFixes.FixAllScope; +using CodeRefactoringsFixAllScope = Microsoft.CodeAnalysis.CodeRefactorings.FixAllScope; + namespace Roslyn.VisualStudio.IntegrationTests.InProcess { [TestService] @@ -113,10 +115,13 @@ public async Task CodeActionAsync( bool applyFix = false, bool verifyNotShowing = false, bool ensureExpectedItemsAreOrdered = false, - FixAllScope? fixAllScope = null, + CodeFixesFixAllScope? fixAllScope = null, + CodeRefactoringsFixAllScope? refactoringFixAllScope = null, bool blockUntilComplete = true, CancellationToken cancellationToken = default) { + Assert.True(fixAllScope == null || refactoringFixAllScope == null); + var expectedItems = new[] { expectedItem }; bool? applied; @@ -125,7 +130,7 @@ public async Task CodeActionAsync( cancellationToken.ThrowIfCancellationRequested(); applied = await CodeActionsAsync(expectedItems, applyFix ? expectedItem : null, verifyNotShowing, - ensureExpectedItemsAreOrdered, fixAllScope, blockUntilComplete, cancellationToken); + ensureExpectedItemsAreOrdered, fixAllScope, refactoringFixAllScope, blockUntilComplete, cancellationToken); } while (applied is false); } @@ -141,10 +146,13 @@ public async Task CodeActionAsync( string? applyFix = null, bool verifyNotShowing = false, bool ensureExpectedItemsAreOrdered = false, - FixAllScope? fixAllScope = null, + CodeFixesFixAllScope? fixAllScope = null, + CodeRefactoringsFixAllScope? refactoringFixAllScope = null, bool blockUntilComplete = true, CancellationToken cancellationToken = default) { + Assert.True(fixAllScope == null || refactoringFixAllScope == null); + var events = new List(); void WorkspaceChangedHandler(object sender, WorkspaceChangeEventArgs e) => events.Add(e); @@ -177,7 +185,7 @@ public async Task CodeActionAsync( } } - if (fixAllScope.HasValue) + if (fixAllScope.HasValue || refactoringFixAllScope.HasValue) { Assumes.Present(applyFix); } @@ -187,7 +195,7 @@ public async Task CodeActionAsync( var codeActionLogger = new CodeActionLogger(); using var loggerRestorer = WithLogger(AggregateLogger.AddOrReplace(codeActionLogger, Logger.GetLogger(), logger => logger is CodeActionLogger)); - var result = await TestServices.Editor.ApplyLightBulbActionAsync(applyFix, fixAllScope, blockUntilComplete, cancellationToken); + var result = await TestServices.Editor.ApplyLightBulbActionAsync(applyFix, fixAllScope, refactoringFixAllScope, blockUntilComplete, cancellationToken); if (blockUntilComplete) { From bd85c931fe076b2495aa4da275d86ba43ed7f8aa Mon Sep 17 00:00:00 2001 From: Manish Vasani Date: Thu, 14 Apr 2022 02:43:40 -0700 Subject: [PATCH 17/18] Fixes from merge --- .../FixAll/FixAllCodeRefactoringGetFixesService.cs | 2 +- .../UnifiedSuggestions/UnifiedSuggestedActionsSource.cs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/EditorFeatures/Core.Wpf/Suggestions/FixAll/FixAllCodeRefactoringGetFixesService.cs b/src/EditorFeatures/Core.Wpf/Suggestions/FixAll/FixAllCodeRefactoringGetFixesService.cs index 31e27d9c2c1e0..54e1fa0d6ef19 100644 --- a/src/EditorFeatures/Core.Wpf/Suggestions/FixAll/FixAllCodeRefactoringGetFixesService.cs +++ b/src/EditorFeatures/Core.Wpf/Suggestions/FixAll/FixAllCodeRefactoringGetFixesService.cs @@ -55,7 +55,7 @@ public async Task> GetFixAllOperationsAsync( private static async Task GetFixAllCodeActionAsync(FixAllContext fixAllContext) { using (Logger.LogBlock( - FunctionId.CodeFixes_FixAllOccurrencesComputation, + FunctionId.Refactoring_FixAllOccurrencesComputation, KeyValueLogMessage.Create(LogType.UserAction, m => { m[FixAllLogger.CorrelationId] = fixAllContext.State.CorrelationId; diff --git a/src/Features/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActionsSource.cs b/src/Features/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActionsSource.cs index 76b4d210f5d67..1d1f440eec579 100644 --- a/src/Features/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActionsSource.cs +++ b/src/Features/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActionsSource.cs @@ -32,7 +32,7 @@ public static ValueTask> GetFilterAndO Document document, TextSpan selection, CodeActionRequestPriority priority, - CodeActionOptions options, + CodeActionOptionsProvider options, Func addOperationScope, CancellationToken cancellationToken) => CodeFixesComputer.GetFilterAndOrderCodeFixesAsync(workspace, codeFixService, document, selection, priority, options, addOperationScope, cancellationToken); @@ -46,7 +46,7 @@ public static Task> GetFilterAndOrderC Document document, TextSpan selection, CodeActionRequestPriority priority, - CodeActionOptions options, + CodeActionOptionsProvider options, Func addOperationScope, bool filterOutsideSelection, CancellationToken cancellationToken) From a1e77fe6704b37430303271e6c40dd097bcf51e1 Mon Sep 17 00:00:00 2001 From: Manish Vasani Date: Thu, 14 Apr 2022 05:56:25 -0700 Subject: [PATCH 18/18] Fix IDE0044 --- .../Portable/CodeRefactorings/CodeRefactoringService.cs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/Features/Core/Portable/CodeRefactorings/CodeRefactoringService.cs b/src/Features/Core/Portable/CodeRefactorings/CodeRefactoringService.cs index 58d673d8fbedc..2c7f426d2d665 100644 --- a/src/Features/Core/Portable/CodeRefactorings/CodeRefactoringService.cs +++ b/src/Features/Core/Portable/CodeRefactorings/CodeRefactoringService.cs @@ -96,7 +96,7 @@ public async Task HasRefactoringsAsync( RefactoringToMetadataMap.TryGetValue(provider, out var providerMetadata); var refactoring = await GetRefactoringFromProviderAsync( - document, state, provider, providerMetadata, extensionManager, options, _fixAllProviderMap, cancellationToken).ConfigureAwait(false); + document, state, provider, providerMetadata, extensionManager, options, cancellationToken).ConfigureAwait(false); if (refactoring != null) { @@ -134,7 +134,7 @@ public async Task> GetRefactoringsAsync( using (RoslynEventSource.LogInformationalBlock(FunctionId.Refactoring_CodeRefactoringService_GetRefactoringsAsync, providerName, cancellationToken)) { return GetRefactoringFromProviderAsync(document, state, provider, providerMetadata, - extensionManager, options, _fixAllProviderMap, cancellationToken); + extensionManager, options, cancellationToken); } }, cancellationToken)); @@ -145,14 +145,13 @@ public async Task> GetRefactoringsAsync( } } - private static async Task GetRefactoringFromProviderAsync( + private async Task GetRefactoringFromProviderAsync( Document document, TextSpan state, CodeRefactoringProvider provider, CodeChangeProviderMetadata? providerMetadata, IExtensionManager extensionManager, CodeActionOptionsProvider options, - ImmutableDictionary fixAllProviderMap, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); @@ -189,7 +188,7 @@ public async Task> GetRefactoringsAsync( if (actions.Count > 0) { var fixAllProviderInfo = extensionManager.PerformFunction( - provider, () => ImmutableInterlocked.GetOrAdd(ref fixAllProviderMap, provider, FixAllProviderInfo.Create), defaultValue: null); + provider, () => ImmutableInterlocked.GetOrAdd(ref _fixAllProviderMap, provider, FixAllProviderInfo.Create), defaultValue: null); return new CodeRefactoring(provider, actions.ToImmutable(), fixAllProviderInfo); } else