internal sealed override async Task<CodeAction> GetFixAsync( ImmutableDictionary<Document, ImmutableArray<Diagnostic>> documentsAndDiagnosticsToFixMap, FixAllState fixAllState, CancellationToken cancellationToken) { // Process all documents in parallel. var updatedDocumentTasks = documentsAndDiagnosticsToFixMap.Select( kvp => FixDocumentAsync(kvp.Key, kvp.Value, cancellationToken)); await Task.WhenAll(updatedDocumentTasks).ConfigureAwait(false); var currentSolution = fixAllState.Solution; foreach (var task in updatedDocumentTasks) { // 'await' the tasks so that if any completed in a cancelled manner then we'll // throw the right exception here. Calling .Result on the tasks might end up // with AggregateExceptions being thrown instead. var updatedDocument = await task.ConfigureAwait(false); currentSolution = currentSolution.WithDocumentSyntaxRoot( updatedDocument.Id, await updatedDocument.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false)); } var title = fixAllState.GetDefaultFixAllTitle(); return new CodeAction.SolutionChangeAction(title, _ => Task.FromResult(currentSolution)); }
internal override async Task <CodeAction> GetFixAsync( ImmutableDictionary <Project, ImmutableArray <Diagnostic> > projectsAndDiagnosticsToFixMap, FixAllState fixAllState, CancellationToken cancellationToken) { if (projectsAndDiagnosticsToFixMap != null && projectsAndDiagnosticsToFixMap.Any()) { FixAllLogger.LogDiagnosticsStats(projectsAndDiagnosticsToFixMap); var fixesBag = new ConcurrentBag <CodeAction>(); using (Logger.LogBlock(FunctionId.CodeFixes_FixAllOccurrencesComputation_Fixes, cancellationToken)) { var projects = projectsAndDiagnosticsToFixMap.Keys; var tasks = projects.Select(p => AddProjectFixesAsync(p, projectsAndDiagnosticsToFixMap[p], fixesBag.Add, fixAllState, cancellationToken)) .ToArray(); await Task.WhenAll(tasks).ConfigureAwait(false); } if (fixesBag.Any()) { using (Logger.LogBlock(FunctionId.CodeFixes_FixAllOccurrencesComputation_Merge, cancellationToken)) { FixAllLogger.LogFixesToMergeStats(fixesBag); return(await TryGetMergedFixAsync(fixesBag, fixAllState, cancellationToken).ConfigureAwait(false)); } } } return(null); }
public virtual string GetFixAllTitle(FixAllState fixAllState) { var diagnosticIds = fixAllState.DiagnosticIds; string diagnosticId; if (diagnosticIds.Count() == 1) { diagnosticId = diagnosticIds.Single(); } else { diagnosticId = string.Join(",", diagnosticIds.ToArray()); } switch (fixAllState.Scope) { case FixAllScope.Custom: return(string.Format(WorkspacesResources.FixAllOccurrencesOfDiagnostic, diagnosticId)); case FixAllScope.Document: var document = fixAllState.Document; return(string.Format(WorkspacesResources.FixAllOccurrencesOfDiagnosticInScope, diagnosticId, document.Name)); case FixAllScope.Project: var project = fixAllState.Project; return(string.Format(WorkspacesResources.FixAllOccurrencesOfDiagnosticInScope, diagnosticId, project.Name)); case FixAllScope.Solution: return(string.Format(WorkspacesResources.FixAllOccurrencesOfDiagnosticInSolution, diagnosticId)); default: throw ExceptionUtilities.Unreachable; } }
public virtual async Task AddProjectFixesAsync( Project project, ImmutableArray <Diagnostic> diagnostics, Action <CodeAction> addFix, FixAllState fixAllState, CancellationToken cancellationToken) { Debug.Assert(!diagnostics.IsDefault); cancellationToken.ThrowIfCancellationRequested(); var fixes = new List <CodeAction>(); var context = new CodeFixContext(project, diagnostics, // TODO: Can we share code between similar lambdas that we pass to this API in BatchFixAllProvider.cs, CodeFixService.cs and CodeRefactoringService.cs? (a, d) => { // Serialize access for thread safety - we don't know what thread the fix provider will call this delegate from. lock (fixes) { fixes.Add(a); } }, cancellationToken); // TODO: Wrap call to ComputeFixesAsync() below in IExtensionManager.PerformFunctionAsync() so that // a buggy extension that throws can't bring down the host? var task = fixAllState.CodeFixProvider.RegisterCodeFixesAsync(context) ?? SpecializedTasks.EmptyTask; await task.ConfigureAwait(false); foreach (var fix in fixes) { cancellationToken.ThrowIfCancellationRequested(); if (fix != null && fix.EquivalenceKey == fixAllState.CodeActionEquivalenceKey) { addFix(fix); } } }
internal sealed override async Task <CodeAction> GetFixAsync( ImmutableDictionary <Document, ImmutableArray <Diagnostic> > documentsAndDiagnosticsToFixMap, FixAllState fixAllState, CancellationToken cancellationToken) { // Process all documents in parallel. var updatedDocumentTasks = documentsAndDiagnosticsToFixMap.Select( kvp => FixDocumentAsync(kvp.Key, kvp.Value, cancellationToken)); await Task.WhenAll(updatedDocumentTasks).ConfigureAwait(false); var currentSolution = fixAllState.Solution; foreach (var task in updatedDocumentTasks) { // 'await' the tasks so that if any completed in a canceled manner then we'll // throw the right exception here. Calling .Result on the tasks might end up // with AggregateExceptions being thrown instead. var updatedDocument = await task.ConfigureAwait(false); currentSolution = currentSolution.WithDocumentSyntaxRoot( updatedDocument.Id, await updatedDocument.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false)); } var title = fixAllState.GetDefaultFixAllTitle(); return(new CodeAction.SolutionChangeAction(title, _ => Task.FromResult(currentSolution))); }
internal override async Task <CodeAction> GetFixAsync( ImmutableDictionary <Document, ImmutableArray <Diagnostic> > documentsAndDiagnosticsToFixMap, FixAllState fixAllState, CancellationToken cancellationToken) { if (documentsAndDiagnosticsToFixMap != null && documentsAndDiagnosticsToFixMap.Any()) { FixAllLogger.LogDiagnosticsStats(documentsAndDiagnosticsToFixMap); var fixesBag = new ConcurrentBag <(Diagnostic diagnostic, CodeAction action)>(); using (Logger.LogBlock(FunctionId.CodeFixes_FixAllOccurrencesComputation_Fixes, cancellationToken)) { cancellationToken.ThrowIfCancellationRequested(); var documents = documentsAndDiagnosticsToFixMap.Keys; var tasks = documents.Select(d => AddDocumentFixesAsync( d, documentsAndDiagnosticsToFixMap[d], fixesBag, fixAllState, cancellationToken)).ToArray(); await Task.WhenAll(tasks).ConfigureAwait(false); } if (fixesBag.Count > 0) { using (Logger.LogBlock(FunctionId.CodeFixes_FixAllOccurrencesComputation_Merge, cancellationToken)) { FixAllLogger.LogFixesToMergeStats(fixesBag.Count); return(await TryGetMergedFixAsync( fixesBag.ToImmutableArray(), fixAllState, cancellationToken).ConfigureAwait(false)); } } } return(null); }
private async Task <Document> AddSimplifierAnnotationsAsync( Document document, ImmutableArray <Diagnostic> diagnostics, FixAllState fixAllState, CancellationToken cancellationToken) { var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); var model = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); // Find all nodes to simplify corresponding to diagnostic spans. var nodesToSimplify = new List <SyntaxNode>(); foreach (var diagnostic in diagnostics) { string codeActionEquivalenceKey; var node = GetNodeToSimplify(root, model, diagnostic, document, out codeActionEquivalenceKey, cancellationToken); if (node != null && fixAllState.CodeActionEquivalenceKey == codeActionEquivalenceKey) { nodesToSimplify.Add(node); } } // Add simplifier and formatter annotations to all nodes to simplify. // If the fix all provider needs to fixup any of the parent nodes, then we iterate through each of the nodesToSimplify // and fixup any parenting node, computing a new document with required simplifier annotations in each iteration. // Otherwise, if the fix all provider doesn't need parent fixup, we just add simplifier annotation to all nodesToSimplify. if (!NeedsParentFixup) { root = root.ReplaceNodes(nodesToSimplify, (o, n) => n.WithAdditionalAnnotations(Simplifier.Annotation, Formatter.Annotation)); } else { // Add a custom annotation to nodesToSimplify so we can get back to them later. var annotation = new SyntaxAnnotation(); root = root.ReplaceNodes(nodesToSimplify, (o, n) => o.WithAdditionalAnnotations(annotation)); document = document.WithSyntaxRoot(root); while (true) { root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); var annotatedNodes = root.GetAnnotatedNodes(annotation); // Get the next un-processed node to simplify, processed nodes should have simplifier annotation. var annotatedNode = annotatedNodes.FirstOrDefault(n => !n.HasAnnotation(Simplifier.Annotation)); if (annotatedNode == null) { // All nodesToSimplify have been processed. // Remove all the custom annotations added for tracking nodesToSimplify. root = root.ReplaceNodes(annotatedNodes, (o, n) => o.WithoutAnnotations(annotation)); break; } document = await AddSimplifyAnnotationsAsync(document, annotatedNode, cancellationToken).ConfigureAwait(false); } } return(document.WithSyntaxRoot(root)); }
private async Task <CodeAction> GetFixAsync( ImmutableDictionary <Document, ImmutableArray <Diagnostic> > documentsAndDiagnosticsToFixMap, FixAllState fixAllState, CancellationToken cancellationToken) { if (documentsAndDiagnosticsToFixMap?.Any() == true) { FixAllLogger.LogDiagnosticsStats(fixAllState.CorrelationId, documentsAndDiagnosticsToFixMap); var diagnosticsAndCodeActions = await GetDiagnosticsAndCodeActionsAsync( documentsAndDiagnosticsToFixMap, fixAllState, cancellationToken).ConfigureAwait(false); if (diagnosticsAndCodeActions.Length > 0) { var functionId = FunctionId.CodeFixes_FixAllOccurrencesComputation_Document_Merge; using (Logger.LogBlock(functionId, FixAllLogger.CreateCorrelationLogMessage(fixAllState.CorrelationId), cancellationToken)) { FixAllLogger.LogFixesToMergeStats(functionId, fixAllState.CorrelationId, diagnosticsAndCodeActions.Length); return(await TryGetMergedFixAsync( diagnosticsAndCodeActions, fixAllState, cancellationToken).ConfigureAwait(false)); } } } return(null); }
private async Task <ImmutableArray <(Diagnostic diagnostic, CodeAction action)> > GetDiagnosticsAndCodeActionsAsync( ImmutableDictionary <Document, ImmutableArray <Diagnostic> > documentsAndDiagnosticsToFixMap, FixAllState fixAllState, CancellationToken cancellationToken) { var fixesBag = new ConcurrentBag <(Diagnostic diagnostic, CodeAction action)>(); using (Logger.LogBlock( FunctionId.CodeFixes_FixAllOccurrencesComputation_Document_Fixes, FixAllLogger.CreateCorrelationLogMessage(fixAllState.CorrelationId), cancellationToken)) { cancellationToken.ThrowIfCancellationRequested(); var tasks = new List <Task>(); foreach (var kvp in documentsAndDiagnosticsToFixMap) { var document = kvp.Key; var diagnosticsToFix = kvp.Value; Debug.Assert(!diagnosticsToFix.IsDefaultOrEmpty); if (!diagnosticsToFix.IsDefaultOrEmpty) { tasks.Add(AddDocumentFixesAsync( document, diagnosticsToFix, fixesBag, fixAllState, cancellationToken)); } } await Task.WhenAll(tasks).ConfigureAwait(false); } return(fixesBag.ToImmutableArray()); }
internal override async Task<CodeAction> GetFixAsync( ImmutableDictionary<Document, ImmutableArray<Diagnostic>> documentsAndDiagnosticsToFixMap, FixAllState fixAllState, CancellationToken cancellationToken) { if (documentsAndDiagnosticsToFixMap != null && documentsAndDiagnosticsToFixMap.Any()) { FixAllLogger.LogDiagnosticsStats(documentsAndDiagnosticsToFixMap); var fixesBag = new ConcurrentBag<(Diagnostic diagnostic, CodeAction action)>(); using (Logger.LogBlock(FunctionId.CodeFixes_FixAllOccurrencesComputation_Fixes, cancellationToken)) { cancellationToken.ThrowIfCancellationRequested(); var documents = documentsAndDiagnosticsToFixMap.Keys; var tasks = documents.Select(d => AddDocumentFixesAsync( d, documentsAndDiagnosticsToFixMap[d], fixesBag, fixAllState, cancellationToken)).ToArray(); await Task.WhenAll(tasks).ConfigureAwait(false); } if (fixesBag.Count > 0) { using (Logger.LogBlock(FunctionId.CodeFixes_FixAllOccurrencesComputation_Merge, cancellationToken)) { FixAllLogger.LogFixesToMergeStats(fixesBag.Count); return await TryGetMergedFixAsync( fixesBag.ToImmutableArray(), fixAllState, cancellationToken).ConfigureAwait(false); } } } return null; }
public static void LogState(FixAllState fixAllState, bool isInternalCodeFixProvider) { Logger.Log(FunctionId.CodeFixes_FixAllOccurrencesContext, KeyValueLogMessage.Create(m => { m[CorrelationId] = fixAllState.CorrelationId; if (isInternalCodeFixProvider) { m[CodeFixProvider] = fixAllState.CodeFixProvider.GetType().FullName !; m[CodeActionEquivalenceKey] = fixAllState.CodeActionEquivalenceKey; m[LanguageName] = fixAllState.Project.Language; } else { m[CodeFixProvider] = fixAllState.CodeFixProvider.GetType().FullName !.GetHashCode().ToString(); m[CodeActionEquivalenceKey] = fixAllState.CodeActionEquivalenceKey?.GetHashCode().ToString(); m[LanguageName] = fixAllState.Project.Language.GetHashCode().ToString(); } m[FixAllScope] = fixAllState.Scope.ToString(); switch (fixAllState.Scope) { case CodeFixes.FixAllScope.Project: m[DocumentCount] = fixAllState.Project.DocumentIds.Count; break; case CodeFixes.FixAllScope.Solution: m[DocumentCount] = fixAllState.Solution.Projects.Sum(p => p.DocumentIds.Count); break; } })); }
public static void LogState(FixAllState fixAllState, bool isInternalCodeFixProvider) { Logger.Log(FunctionId.CodeFixes_FixAllOccurrencesContext, KeyValueLogMessage.Create(m => { if (isInternalCodeFixProvider) { m[s_codeFixProvider] = fixAllState.CodeFixProvider.GetType().FullName; m[s_codeActionEquivalenceKey] = fixAllState.CodeActionEquivalenceKey; m[s_languageName] = fixAllState.Project.Language; } else { m[s_codeFixProvider] = fixAllState.CodeFixProvider.GetType().FullName.GetHashCode().ToString(); m[s_codeActionEquivalenceKey] = fixAllState.CodeActionEquivalenceKey != null ? fixAllState.CodeActionEquivalenceKey.GetHashCode().ToString() : null; m[s_languageName] = fixAllState.Project.Language.GetHashCode().ToString(); } m[s_fixAllScope] = fixAllState.Scope.ToString(); switch (fixAllState.Scope) { case CodeFixes.FixAllScope.Project: m[s_documentCount] = fixAllState.Project.DocumentIds.Count; break; case CodeFixes.FixAllScope.Solution: m[s_documentCount] = fixAllState.Solution.Projects.Sum(p => p.DocumentIds.Count); break; } })); }
internal FixAllContext( FixAllState state, CancellationToken cancellationToken) { State = state; this.CancellationToken = cancellationToken; }
private async Task<Document> AddSimplifierAnnotationsAsync( Document document, ImmutableArray<Diagnostic> diagnostics, FixAllState fixAllState, CancellationToken cancellationToken) { var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); var model = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); var options = await document.GetOptionsAsync(cancellationToken).ConfigureAwait(false); // Find all nodes to simplify corresponding to diagnostic spans. var nodesToSimplify = new List<SyntaxNode>(); foreach (var diagnostic in diagnostics) { string codeActionEquivalenceKey; var node = GetNodeToSimplify(root, model, diagnostic, options, out codeActionEquivalenceKey, cancellationToken); if (node != null && fixAllState.CodeActionEquivalenceKey == codeActionEquivalenceKey) { nodesToSimplify.Add(node); } } // Add simplifier and formatter annotations to all nodes to simplify. // If the fix all provider needs to fixup any of the parent nodes, then we iterate through each of the nodesToSimplify // and fixup any parenting node, computing a new document with required simplifier annotations in each iteration. // Otherwise, if the fix all provider doesn't need parent fixup, we just add simplifier annotation to all nodesToSimplify. if (!NeedsParentFixup) { root = root.ReplaceNodes(nodesToSimplify, (o, n) => n.WithAdditionalAnnotations(Simplifier.Annotation, Formatter.Annotation)); } else { // Add a custom annotation to nodesToSimplify so we can get back to them later. var annotation = new SyntaxAnnotation(); root = root.ReplaceNodes(nodesToSimplify, (o, n) => o.WithAdditionalAnnotations(annotation)); document = document.WithSyntaxRoot(root); while (true) { root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); var annotatedNodes = root.GetAnnotatedNodes(annotation); // Get the next un-processed node to simplify, processed nodes should have simplifier annotation. var annotatedNode = annotatedNodes.FirstOrDefault(n => !n.HasAnnotation(Simplifier.Annotation)); if (annotatedNode == null) { // All nodesToSimplify have been processed. // Remove all the custom annotations added for tracking nodesToSimplify. root = root.ReplaceNodes(annotatedNodes, (o, n) => o.WithoutAnnotations(annotation)); break; } document = await AddSimplifyAnnotationsAsync(document, annotatedNode, cancellationToken).ConfigureAwait(false); } } return document.WithSyntaxRoot(root); }
internal FixAllContext( FixAllState state, IProgressTracker progressTracker, CancellationToken cancellationToken) { State = state; this.ProgressTracker = progressTracker; this.CancellationToken = cancellationToken; }
public override async Task AddDocumentFixesAsync( Document document, ImmutableArray<Diagnostic> diagnostics, Action<CodeAction> addFix, FixAllState fixAllState, CancellationToken cancellationToken) { var changedDocument = await AddSimplifierAnnotationsAsync( document, diagnostics, fixAllState, cancellationToken).ConfigureAwait(false); var title = GetFixAllTitle(fixAllState); var codeAction = new MyCodeAction(title, (c) => Task.FromResult(changedDocument)); addFix(codeAction); }
public CodeFixCollection( object provider, TextSpan span, IEnumerable<CodeFix> fixes, FixAllState fixAllState, IEnumerable<FixAllScope> supportedScopes, Diagnostic firstDiagnostic) : this(provider, span, fixes.ToImmutableArray(), fixAllState, supportedScopes, firstDiagnostic) { }
public CodeFixCollection( object provider, TextSpan span, IEnumerable <CodeFix> fixes, FixAllState fixAllState, IEnumerable <FixAllScope> supportedScopes, Diagnostic firstDiagnostic) : this(provider, span, fixes.ToImmutableArray(), fixAllState, supportedScopes, firstDiagnostic) { }
private async Task AppendFixesOrSuppressionsAsync( Document document, TextSpan span, IEnumerable <DiagnosticData> diagnosticsWithSameSpan, ArrayBuilder <CodeFixCollection> result, object fixer, Func <Diagnostic, bool> hasFix, Func <ImmutableArray <Diagnostic>, Task <ImmutableArray <CodeFix> > > getFixes, CancellationToken cancellationToken) { var allDiagnostics = await diagnosticsWithSameSpan.OrderByDescending(d => d.Severity) .ToDiagnosticsAsync(document.Project, cancellationToken).ConfigureAwait(false); var diagnostics = allDiagnostics.WhereAsArray(hasFix); if (diagnostics.Length <= 0) { // this can happen for suppression case where all diagnostics can't be suppressed return; } var extensionManager = document.Project.Solution.Workspace.Services.GetService <IExtensionManager>(); var fixes = await extensionManager.PerformFunctionAsync(fixer, () => getFixes(diagnostics), defaultValue : ImmutableArray <CodeFix> .Empty).ConfigureAwait(false); if (fixes.IsDefaultOrEmpty) { return; } // If the fix provider supports fix all occurrences, then get the corresponding FixAllProviderInfo and fix all context. var fixAllProviderInfo = extensionManager.PerformFunction(fixer, () => ImmutableInterlocked.GetOrAdd(ref _fixAllProviderMap, fixer, FixAllProviderInfo.Create), defaultValue: null); FixAllState fixAllState = null; var supportedScopes = ImmutableArray <FixAllScope> .Empty; if (fixAllProviderInfo != null) { var codeFixProvider = (fixer as CodeFixProvider) ?? new WrapperCodeFixProvider((ISuppressionFixProvider)fixer, diagnostics.Select(d => d.Id)); fixAllState = CreateFixAllState( fixAllProviderInfo.FixAllProvider, document, fixAllProviderInfo, codeFixProvider, diagnostics, this.GetDocumentDiagnosticsAsync, this.GetProjectDiagnosticsAsync); supportedScopes = fixAllProviderInfo.SupportedScopes; } var codeFix = new CodeFixCollection( fixer, span, fixes, fixAllState, supportedScopes, diagnostics.First()); result.Add(codeFix); }
private Task <Document> FixDocumentAsync( FixAllState fixAllState, Document document, ImmutableArray <Diagnostic> diagnostics, CancellationToken cancellationToken) { // Ensure that diagnostics for this document are always in document location // order. This provides a consistent and deterministic order for fixers // that want to update a document. var filteredDiagnostics = diagnostics.WhereAsArray(d => _codeFixProvider.IncludeDiagnosticDuringFixAll(fixAllState, d)) .Sort((d1, d2) => d1.Location.SourceSpan.Start - d2.Location.SourceSpan.Start); return(_codeFixProvider.FixAllAsync(document, filteredDiagnostics, cancellationToken)); }
public override async Task AddDocumentFixesAsync( Document document, ImmutableArray <Diagnostic> diagnostics, Action <CodeAction> addFix, FixAllState fixAllState, CancellationToken cancellationToken) { var changedDocument = await AddSimplifierAnnotationsAsync( document, diagnostics, fixAllState, cancellationToken).ConfigureAwait(false); var title = GetFixAllTitle(fixAllState); var codeAction = new MyCodeAction(title, (c) => Task.FromResult(changedDocument)); addFix(codeAction); }
private FixMultipleSuggestedAction GetSuggestedAction( FixAllState fixAllState, Diagnostic triggerDiagnostic, Workspace workspace, string title, string waitDialogMessage, bool showPreviewChangesDialog, CancellationToken cancellationToken) { var fixMultipleCodeAction = new FixMultipleCodeAction(fixAllState, triggerDiagnostic, title, waitDialogMessage, showPreviewChangesDialog); return new FixMultipleSuggestedAction( _listener, workspace, _editHandler, _waitIndicator, fixMultipleCodeAction, fixAllState.FixAllProvider); }
public CodeFixCollection( object provider, TextSpan span, ImmutableArray <CodeFix> fixes, FixAllState fixAllState, IEnumerable <FixAllScope> supportedScopes, Diagnostic firstDiagnostic) { this.Provider = provider; this.TextSpan = span; this.Fixes = fixes; this.FixAllState = fixAllState; this.SupportedScopes = supportedScopes; this.FirstDiagnostic = firstDiagnostic; }
public CodeFixCollection( object provider, TextSpan span, ImmutableArray <CodeFix> fixes, FixAllState fixAllState, ImmutableArray <FixAllScope> supportedScopes, Diagnostic firstDiagnostic) { Provider = provider; TextSpan = span; Fixes = fixes.NullToEmpty(); FixAllState = fixAllState; SupportedScopes = supportedScopes.NullToEmpty(); FirstDiagnostic = firstDiagnostic; }
public CodeFixCollection( object provider, TextSpan span, ImmutableArray<CodeFix> fixes, FixAllState fixAllState, IEnumerable<FixAllScope> supportedScopes, Diagnostic firstDiagnostic) { this.Provider = provider; this.TextSpan = span; this.Fixes = fixes; this.FixAllState = fixAllState; this.SupportedScopes = supportedScopes; this.FirstDiagnostic = firstDiagnostic; }
public CodeFixCollection( object provider, TextSpan span, ImmutableArray<CodeFix> fixes, FixAllState fixAllState, ImmutableArray<FixAllScope> supportedScopes, Diagnostic firstDiagnostic) { Provider = provider; TextSpan = span; Fixes = fixes.NullToEmpty(); FixAllState = fixAllState; SupportedScopes = supportedScopes.NullToEmpty(); FirstDiagnostic = firstDiagnostic; }
public async virtual Task AddDocumentFixesAsync( Document document, ImmutableArray<Diagnostic> diagnostics, Action<CodeAction> addFix, FixAllState fixAllState, CancellationToken cancellationToken) { Debug.Assert(!diagnostics.IsDefault); cancellationToken.ThrowIfCancellationRequested(); var fixerTasks = new Task[diagnostics.Length]; for (var i = 0; i < diagnostics.Length; i++) { cancellationToken.ThrowIfCancellationRequested(); var diagnostic = diagnostics[i]; fixerTasks[i] = Task.Run(async () => { var fixes = new List<CodeAction>(); var context = new CodeFixContext(document, diagnostic, // TODO: Can we share code between similar lambdas that we pass to this API in BatchFixAllProvider.cs, CodeFixService.cs and CodeRefactoringService.cs? (a, d) => { // Serialize access for thread safety - we don't know what thread the fix provider will call this delegate from. lock (fixes) { fixes.Add(a); } }, cancellationToken); // TODO: Wrap call to ComputeFixesAsync() below in IExtensionManager.PerformFunctionAsync() so that // a buggy extension that throws can't bring down the host? var task = fixAllState.CodeFixProvider.RegisterCodeFixesAsync(context) ?? SpecializedTasks.EmptyTask; await task.ConfigureAwait(false); foreach (var fix in fixes) { cancellationToken.ThrowIfCancellationRequested(); if (fix != null && fix.EquivalenceKey == fixAllState.CodeActionEquivalenceKey) { addFix(fix); } } }); } await Task.WhenAll(fixerTasks).ConfigureAwait(false); }
public virtual async Task <CodeAction> TryGetMergedFixAsync( IEnumerable <CodeAction> batchOfFixes, FixAllState fixAllState, CancellationToken cancellationToken) { Contract.ThrowIfNull(batchOfFixes); Contract.ThrowIfFalse(batchOfFixes.Any()); var solution = fixAllState.Solution; var newSolution = await TryMergeFixesAsync(solution, batchOfFixes, fixAllState, cancellationToken).ConfigureAwait(false); if (newSolution != null && newSolution != solution) { var title = GetFixAllTitle(fixAllState); return(new CodeAction.SolutionChangeAction(title, _ => Task.FromResult(newSolution))); } return(null); }
private async Task <Document> FixDocumentAsync( FixAllState fixAllState, Document document, ImmutableArray <Diagnostic> diagnostics, CancellationToken cancellationToken) { // Ensure that diagnostics for this document are always in document location // order. This provides a consistent and deterministic order for fixers // that want to update a document. // Also ensure that we do not pass in duplicates by invoking Distinct. // See https://github.com/dotnet/roslyn/issues/31381, that seems to be causing duplicate diagnostics. var filteredDiagnostics = diagnostics.Distinct() .WhereAsArray(d => _codeFixProvider.IncludeDiagnosticDuringFixAll(fixAllState, d, cancellationToken)) .Sort((d1, d2) => d1.Location.SourceSpan.Start - d2.Location.SourceSpan.Start); // PERF: Do not invoke FixAllAsync on the code fix provider if there are no diagnostics to be fixed. if (filteredDiagnostics.Length == 0) { return(document); } return(await _codeFixProvider.FixAllAsync(document, filteredDiagnostics, cancellationToken).ConfigureAwait(false)); }
private Solution GetFixedSolution( FixAllState fixAllState, Workspace workspace, string title, string waitDialogMessage, CancellationToken cancellationToken) { var fixMultipleCodeAction = new FixMultipleCodeAction( fixAllState, title, waitDialogMessage); Solution newSolution = null; var extensionManager = workspace.Services.GetService<IExtensionManager>(); extensionManager.PerformAction(fixAllState.FixAllProvider, () => { // We don't need to post process changes here as the inner code action created for Fix multiple code fix already executes. newSolution = fixMultipleCodeAction.GetChangedSolutionInternalAsync( postProcessChanges: false, cancellationToken: cancellationToken).WaitAndGetResult(cancellationToken); }); return newSolution; }
internal override async Task <CodeAction> GetFixAsync( ImmutableDictionary <Document, ImmutableArray <Diagnostic> > documentsAndDiagnosticsToFixMap, FixAllState fixAllState, CancellationToken cancellationToken) { if (documentsAndDiagnosticsToFixMap?.Any() == true) { FixAllLogger.LogDiagnosticsStats(documentsAndDiagnosticsToFixMap); var diagnosticsAndCodeActions = await GetDiagnosticsAndCodeActions( documentsAndDiagnosticsToFixMap, fixAllState, cancellationToken).ConfigureAwait(false); if (diagnosticsAndCodeActions.Length > 0) { using (Logger.LogBlock(FunctionId.CodeFixes_FixAllOccurrencesComputation_Merge, cancellationToken)) { FixAllLogger.LogFixesToMergeStats(diagnosticsAndCodeActions.Length); return(await TryGetMergedFixAsync( diagnosticsAndCodeActions, fixAllState, cancellationToken).ConfigureAwait(false)); } } } return(null); }
public virtual string GetFixAllTitle(FixAllState fixAllState) { return(fixAllState.GetDefaultFixAllTitle()); }
/// <summary> /// Whether or not this diagnostic should be included when performing a FixAll. This is /// useful for providers that create multiple diagnostics for the same issue (For example, /// one main diagnostic and multiple 'faded out code' diagnostics). FixAll can be invoked /// from any of those, but we'll only want perform an edit for only one diagnostic for each /// of those sets of diagnostics. /// /// This overload differs from <see cref="IncludeDiagnosticDuringFixAll(Diagnostic)"/> in /// that it also passes along the <see cref="FixAllState"/> in case that would be useful /// (for example if the <see cref="FixAllState.CodeActionEquivalenceKey"/> is used. /// /// Only one of these two overloads needs to be overridden if you want to customize /// behavior. /// </summary> protected virtual bool IncludeDiagnosticDuringFixAll(FixAllState fixAllState, Diagnostic diagnostic, CancellationToken cancellationToken) => IncludeDiagnosticDuringFixAll(diagnostic);
internal FixSomeCodeAction( FixAllState fixAllState, bool showPreviewChangesDialog) { _fixAllState = fixAllState; _showPreviewChangesDialog = showPreviewChangesDialog; }
internal override async Task<CodeAction> GetFixAsync( ImmutableDictionary<Project, ImmutableArray<Diagnostic>> projectsAndDiagnosticsToFixMap, FixAllState fixAllState, CancellationToken cancellationToken) { if (projectsAndDiagnosticsToFixMap != null && projectsAndDiagnosticsToFixMap.Any()) { FixAllLogger.LogDiagnosticsStats(projectsAndDiagnosticsToFixMap); var fixesBag = new ConcurrentBag<CodeAction>(); using (Logger.LogBlock(FunctionId.CodeFixes_FixAllOccurrencesComputation_Fixes, cancellationToken)) { var projects = projectsAndDiagnosticsToFixMap.Keys; var tasks = projects.Select(p => AddProjectFixesAsync(p, projectsAndDiagnosticsToFixMap[p], fixesBag.Add, fixAllState, cancellationToken)) .ToArray(); await Task.WhenAll(tasks).ConfigureAwait(false); } if (fixesBag.Any()) { using (Logger.LogBlock(FunctionId.CodeFixes_FixAllOccurrencesComputation_Merge, cancellationToken)) { FixAllLogger.LogFixesToMergeStats(fixesBag); return await TryGetMergedFixAsync(fixesBag, fixAllState, cancellationToken).ConfigureAwait(false); } } } return null; }
public virtual async Task<CodeAction> TryGetMergedFixAsync( IEnumerable<CodeAction> batchOfFixes, FixAllState fixAllState, CancellationToken cancellationToken) { Contract.ThrowIfNull(batchOfFixes); Contract.ThrowIfFalse(batchOfFixes.Any()); var solution = fixAllState.Solution; var newSolution = await TryMergeFixesAsync(solution, batchOfFixes, fixAllState, cancellationToken).ConfigureAwait(false); if (newSolution != null && newSolution != solution) { var title = GetFixAllTitle(fixAllState); return new CodeAction.SolutionChangeAction(title, _ => Task.FromResult(newSolution)); } return null; }
private async Task<IEnumerable<CodeActionOperation>> 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 null; } 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, cancellationToken); if (newSolution == null) { return null; } } // Get a code action, with apply changes operation replaced with the newSolution. return GetNewFixAllOperations(operations, newSolution, cancellationToken); }
public virtual string GetFixAllTitle(FixAllState fixAllState) { return fixAllState.GetDefaultFixAllTitle(); }
internal virtual Task<CodeAction> GetFixAsync( ImmutableDictionary<Project, ImmutableArray<Diagnostic>> projectsAndDiagnosticsToFixMap, FixAllState fixAllState, CancellationToken cancellationToken) { return Task.FromResult<CodeAction>(null); }
public FixAllCodeAction(FixAllState fixAllState) : base(fixAllState, showPreviewChangesDialog: true) { }
internal FixSomeCodeAction( FixAllState fixAllState, bool showPreviewChangesDialog) { FixAllState = fixAllState; _showPreviewChangesDialog = showPreviewChangesDialog; }
public virtual async Task <Solution> TryMergeFixesAsync( Solution oldSolution, IEnumerable <CodeAction> codeActions, FixAllState fixAllState, CancellationToken cancellationToken) { var changedDocumentsMap = new Dictionary <DocumentId, Document>(); Dictionary <DocumentId, List <Document> > documentsToMergeMap = null; foreach (var codeAction in codeActions) { cancellationToken.ThrowIfCancellationRequested(); // TODO: Parallelize GetChangedSolutionInternalAsync for codeActions var changedSolution = await codeAction.GetChangedSolutionInternalAsync(cancellationToken : cancellationToken).ConfigureAwait(false); var solutionChanges = new SolutionChanges(changedSolution, oldSolution); // TODO: Handle added/removed documents // TODO: Handle changed/added/removed additional documents var documentIdsWithChanges = solutionChanges .GetProjectChanges() .SelectMany(p => p.GetChangedDocuments()); foreach (var documentId in documentIdsWithChanges) { cancellationToken.ThrowIfCancellationRequested(); var document = changedSolution.GetDocument(documentId); Document existingDocument; if (changedDocumentsMap.TryGetValue(documentId, out existingDocument)) { if (existingDocument != null) { changedDocumentsMap[documentId] = null; var documentsToMerge = new List <Document>(); documentsToMerge.Add(existingDocument); documentsToMerge.Add(document); documentsToMergeMap = documentsToMergeMap ?? new Dictionary <DocumentId, List <Document> >(); documentsToMergeMap[documentId] = documentsToMerge; } else { documentsToMergeMap[documentId].Add(document); } } else { changedDocumentsMap[documentId] = document; } } } var currentSolution = oldSolution; foreach (var kvp in changedDocumentsMap) { cancellationToken.ThrowIfCancellationRequested(); var document = kvp.Value; if (document != null) { var documentText = await document.GetTextAsync(cancellationToken).ConfigureAwait(false); currentSolution = currentSolution.WithDocumentText(kvp.Key, documentText); } } if (documentsToMergeMap != null) { var mergedDocuments = new ConcurrentDictionary <DocumentId, SourceText>(); var documentsToMergeArray = documentsToMergeMap.ToImmutableArray(); var mergeTasks = new Task[documentsToMergeArray.Length]; for (int i = 0; i < documentsToMergeArray.Length; i++) { cancellationToken.ThrowIfCancellationRequested(); var kvp = documentsToMergeArray[i]; var documentId = kvp.Key; var documentsToMerge = kvp.Value; var oldDocument = oldSolution.GetDocument(documentId); mergeTasks[i] = Task.Run(async() => { var appliedChanges = (await documentsToMerge[0].GetTextChangesAsync(oldDocument, cancellationToken).ConfigureAwait(false)).ToList(); foreach (var document in documentsToMerge.Skip(1)) { cancellationToken.ThrowIfCancellationRequested(); appliedChanges = await TryAddDocumentMergeChangesAsync( oldDocument, document, appliedChanges, cancellationToken).ConfigureAwait(false); } var oldText = await oldDocument.GetTextAsync(cancellationToken).ConfigureAwait(false); var newText = oldText.WithChanges(appliedChanges); mergedDocuments.TryAdd(documentId, newText); }); } await Task.WhenAll(mergeTasks).ConfigureAwait(false); foreach (var kvp in mergedDocuments) { cancellationToken.ThrowIfCancellationRequested(); currentSolution = currentSolution.WithDocumentText(kvp.Key, kvp.Value); } } return(currentSolution); }
public virtual async Task<Solution> TryMergeFixesAsync( Solution oldSolution, IEnumerable<CodeAction> codeActions, FixAllState fixAllState, CancellationToken cancellationToken) { var changedDocumentsMap = new Dictionary<DocumentId, Document>(); Dictionary<DocumentId, List<Document>> documentsToMergeMap = null; foreach (var codeAction in codeActions) { cancellationToken.ThrowIfCancellationRequested(); // TODO: Parallelize GetChangedSolutionInternalAsync for codeActions var changedSolution = await codeAction.GetChangedSolutionInternalAsync(cancellationToken: cancellationToken).ConfigureAwait(false); var solutionChanges = new SolutionChanges(changedSolution, oldSolution); // TODO: Handle added/removed documents // TODO: Handle changed/added/removed additional documents var documentIdsWithChanges = solutionChanges .GetProjectChanges() .SelectMany(p => p.GetChangedDocuments()); foreach (var documentId in documentIdsWithChanges) { cancellationToken.ThrowIfCancellationRequested(); var document = changedSolution.GetDocument(documentId); Document existingDocument; if (changedDocumentsMap.TryGetValue(documentId, out existingDocument)) { if (existingDocument != null) { changedDocumentsMap[documentId] = null; var documentsToMerge = new List<Document>(); documentsToMerge.Add(existingDocument); documentsToMerge.Add(document); documentsToMergeMap = documentsToMergeMap ?? new Dictionary<DocumentId, List<Document>>(); documentsToMergeMap[documentId] = documentsToMerge; } else { documentsToMergeMap[documentId].Add(document); } } else { changedDocumentsMap[documentId] = document; } } } var currentSolution = oldSolution; foreach (var kvp in changedDocumentsMap) { cancellationToken.ThrowIfCancellationRequested(); var document = kvp.Value; if (document != null) { var documentText = await document.GetTextAsync(cancellationToken).ConfigureAwait(false); currentSolution = currentSolution.WithDocumentText(kvp.Key, documentText); } } if (documentsToMergeMap != null) { var mergedDocuments = new ConcurrentDictionary<DocumentId, SourceText>(); var documentsToMergeArray = documentsToMergeMap.ToImmutableArray(); var mergeTasks = new Task[documentsToMergeArray.Length]; for (int i = 0; i < documentsToMergeArray.Length; i++) { cancellationToken.ThrowIfCancellationRequested(); var kvp = documentsToMergeArray[i]; var documentId = kvp.Key; var documentsToMerge = kvp.Value; var oldDocument = oldSolution.GetDocument(documentId); mergeTasks[i] = Task.Run(async () => { var appliedChanges = (await documentsToMerge[0].GetTextChangesAsync(oldDocument, cancellationToken).ConfigureAwait(false)).ToList(); foreach (var document in documentsToMerge.Skip(1)) { cancellationToken.ThrowIfCancellationRequested(); appliedChanges = await TryAddDocumentMergeChangesAsync( oldDocument, document, appliedChanges, cancellationToken).ConfigureAwait(false); } var oldText = await oldDocument.GetTextAsync(cancellationToken).ConfigureAwait(false); var newText = oldText.WithChanges(appliedChanges); mergedDocuments.TryAdd(documentId, newText); }); } await Task.WhenAll(mergeTasks).ConfigureAwait(false); foreach (var kvp in mergedDocuments) { cancellationToken.ThrowIfCancellationRequested(); currentSolution = currentSolution.WithDocumentText(kvp.Key, kvp.Value); } } return currentSolution; }
/// <summary> /// If the provided fix all context is non-null and the context's code action Id matches the given code action's Id then, /// returns the set of fix all occurrences actions associated with the code action. /// </summary> internal static SuggestedActionSet GetFixAllSuggestedActionSet( CodeAction action, int actionCount, FixAllState fixAllState, IEnumerable<FixAllScope> supportedScopes, Diagnostic firstDiagnostic, Workspace workspace, ITextBuffer subjectBuffer, ICodeActionEditHandlerService editHandler, IWaitIndicator waitIndicator, IAsynchronousOperationListener operationListener) { if (fixAllState == null) { return null; } if (actionCount > 1 && action.EquivalenceKey == null) { return null; } var fixAllSuggestedActions = ArrayBuilder<FixAllSuggestedAction>.GetInstance(); foreach (var scope in supportedScopes) { var fixAllStateForScope = fixAllState.WithScopeAndEquivalenceKey(scope, action.EquivalenceKey); var fixAllAction = new FixSomeCodeAction(fixAllStateForScope, showPreviewChangesDialog: true); var fixAllSuggestedAction = new FixAllSuggestedAction( workspace, subjectBuffer, editHandler, waitIndicator, fixAllAction, fixAllStateForScope.FixAllProvider, firstDiagnostic, operationListener); fixAllSuggestedActions.Add(fixAllSuggestedAction); } return new SuggestedActionSet( fixAllSuggestedActions.ToImmutableAndFree(), title: EditorFeaturesResources.Fix_all_occurrences_in); }
private FixAllContext WithState(FixAllState state) => this.State == state ? this : new FixAllContext(state, ProgressTracker, CancellationToken);
internal virtual Task <CodeAction> GetFixAsync( ImmutableDictionary <Project, ImmutableArray <Diagnostic> > projectsAndDiagnosticsToFixMap, FixAllState fixAllState, CancellationToken cancellationToken) { return(Task.FromResult <CodeAction>(null)); }
private async Task AppendFixesOrConfigurationsAsync<TCodeFixProvider>( Document document, TextSpan fixesSpan, IEnumerable<DiagnosticData> diagnosticsWithSameSpan, bool fixAllForInSpan, ArrayBuilder<CodeFixCollection> result, TCodeFixProvider fixer, Func<Diagnostic, bool> hasFix, Func<ImmutableArray<Diagnostic>, Task<ImmutableArray<CodeFix>>> getFixes, CancellationToken cancellationToken) where TCodeFixProvider : notnull { var allDiagnostics = await diagnosticsWithSameSpan.OrderByDescending(d => d.Severity) .ToDiagnosticsAsync(document.Project, cancellationToken).ConfigureAwait(false); var diagnostics = allDiagnostics.WhereAsArray(hasFix); if (diagnostics.Length <= 0) { // this can happen for suppression case where all diagnostics can't be suppressed return; } var extensionManager = document.Project.Solution.Workspace.Services.GetRequiredService<IExtensionManager>(); var fixes = await extensionManager.PerformFunctionAsync(fixer, () => getFixes(diagnostics), defaultValue: ImmutableArray<CodeFix>.Empty).ConfigureAwait(false); if (fixes.IsDefaultOrEmpty) { return; } // If the fix provider supports fix all occurrences, then get the corresponding FixAllProviderInfo and fix all context. var fixAllProviderInfo = extensionManager.PerformFunction<FixAllProviderInfo?>(fixer, () => ImmutableInterlocked.GetOrAdd(ref _fixAllProviderMap, fixer, FixAllProviderInfo.Create), defaultValue: null); FixAllState? fixAllState = null; var supportedScopes = ImmutableArray<FixAllScope>.Empty; if (fixAllProviderInfo != null) { var codeFixProvider = (fixer as CodeFixProvider) ?? new WrapperCodeFixProvider((IConfigurationFixProvider)fixer, diagnostics.Select(d => d.Id)); var diagnosticIds = diagnostics.Where(fixAllProviderInfo.CanBeFixed) .Select(d => d.Id) .ToImmutableHashSet(); var diagnosticProvider = fixAllForInSpan ? new FixAllPredefinedDiagnosticProvider(allDiagnostics) : (FixAllContext.DiagnosticProvider)new FixAllDiagnosticProvider(this, diagnosticIds); fixAllState = new FixAllState( fixAllProvider: fixAllProviderInfo.FixAllProvider, document: document, codeFixProvider: codeFixProvider, scope: FixAllScope.Document, codeActionEquivalenceKey: null, diagnosticIds: diagnosticIds, fixAllDiagnosticProvider: diagnosticProvider); supportedScopes = fixAllProviderInfo.SupportedScopes; } var codeFix = new CodeFixCollection( fixer, fixesSpan, fixes, fixAllState, supportedScopes, diagnostics.First()); result.Add(codeFix); }
public virtual string GetFixAllTitle(FixAllState fixAllState) { var diagnosticIds = fixAllState.DiagnosticIds; string diagnosticId; if (diagnosticIds.Count() == 1) { diagnosticId = diagnosticIds.Single(); } else { diagnosticId = string.Join(",", diagnosticIds.ToArray()); } switch (fixAllState.Scope) { case FixAllScope.Custom: return string.Format(WorkspacesResources.FixAllOccurrencesOfDiagnostic, diagnosticId); case FixAllScope.Document: var document = fixAllState.Document; return string.Format(WorkspacesResources.FixAllOccurrencesOfDiagnosticInScope, diagnosticId, document.Name); case FixAllScope.Project: var project = fixAllState.Project; return string.Format(WorkspacesResources.FixAllOccurrencesOfDiagnosticInScope, diagnosticId, project.Name); case FixAllScope.Solution: return string.Format(WorkspacesResources.FixAllOccurrencesOfDiagnosticInSolution, diagnosticId); default: throw ExceptionUtilities.Unreachable; } }
/// <summary> /// Whether or not this diagnostic should be included when performing a FixAll. This is /// useful for providers that create multiple diagnostics for the same issue (For example, /// one main diagnostic and multiple 'faded out code' diagnostics). FixAll can be invoked /// from any of those, but we'll only want perform an edit for only one diagnostic for each /// of those sets of diagnostics. /// /// This overload differs from <see cref="IncludeDiagnosticDuringFixAll(Diagnostic)"/> in /// that it also passes along the <see cref="FixAllState"/> in case that would be useful /// (for example if the <see cref="FixAllState.CodeActionEquivalenceKey"/> is used. /// /// Only one of these two overloads needs to be overridden if you want to customize /// behavior. /// </summary> protected virtual bool IncludeDiagnosticDuringFixAll(FixAllState fixAllState, Diagnostic diagnostic) => IncludeDiagnosticDuringFixAll(diagnostic);
/// <summary> /// If the provided fix all context is non-null and the context's code action Id matches the given code action's Id then, /// returns the set of fix all occurrences actions associated with the code action. /// </summary> internal SuggestedActionSet GetFixAllSuggestedActionSet( CodeAction action, int actionCount, FixAllState fixAllState, ImmutableArray<FixAllScope> supportedScopes, Diagnostic firstDiagnostic, Workspace workspace) { if (fixAllState == null) { return null; } if (actionCount > 1 && action.EquivalenceKey == null) { return null; } var fixAllSuggestedActions = ArrayBuilder<FixAllSuggestedAction>.GetInstance(); foreach (var scope in supportedScopes) { var fixAllStateForScope = fixAllState.WithScopeAndEquivalenceKey(scope, action.EquivalenceKey); var fixAllSuggestedAction = new FixAllSuggestedAction( _owner, workspace, _subjectBuffer, fixAllStateForScope, firstDiagnostic, action); fixAllSuggestedActions.Add(fixAllSuggestedAction); } return new SuggestedActionSet( fixAllSuggestedActions.ToImmutableAndFree(), title: EditorFeaturesResources.Fix_all_occurrences_in); }