private void AddFix(string codeFixTitle, CodeFixContext context, SyntaxNode root, SyntaxNode classDecl, SyntaxGenerator generator, params string[] languages) { var fix = new MyCodeAction( codeFixTitle, c => GetFix(context.Document, root, classDecl, generator, languages)); context.RegisterCodeFix(fix, context.Diagnostics); }
public override async Task AddDocumentFixesAsync(Document document, ImmutableArray<Diagnostic> diagnostics, Action<CodeAction> addFix, FixAllContext fixAllContext) { var changedDocument = await AddSimplifierAnnotationsAsync(document, diagnostics, fixAllContext).ConfigureAwait(false); var title = GetFixAllTitle(fixAllContext); var codeAction = new MyCodeAction(title, (c) => Task.FromResult(changedDocument)); addFix(codeAction); }
private async Task<Tuple<CodeAction, string>> GetCodeActionAsync( Document document, TextSpan textSpan, CancellationToken cancellationToken) { var result = await ExtractMethodService.ExtractMethodAsync( document, textSpan, cancellationToken: cancellationToken).ConfigureAwait(false); Contract.ThrowIfNull(result); if (result.Succeeded || result.SucceededWithSuggestion) { var documentOptions = await document.GetOptionsAsync(cancellationToken).ConfigureAwait(false); var description = documentOptions.GetOption(ExtractMethodOptions.AllowMovingDeclaration) ? FeaturesResources.Extract_Method_plus_Local : FeaturesResources.Extract_Method; var codeAction = new MyCodeAction(description, (c) => AddRenameAnnotationAsync(result.Document, result.InvocationNameToken, c)); var methodBlock = result.MethodDeclarationNode; return Tuple.Create<CodeAction, string>(codeAction, methodBlock.ToString()); } return null; }
public override async Task RegisterCodeFixesAsync(CodeFixContext context) { var document = context.Document; var span = context.Span; var root = await document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); var node = root.FindNode(span, getInnermostNodeForTie: true); var codeAction = new MyCodeAction( CSharpFeaturesResources.UseExplicitType, c => HandleDeclarationAsync(document, root, node, context.CancellationToken)); context.RegisterCodeFix(codeAction, context.Diagnostics.First()); }
public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) { var document = context.Document; var span = context.Span; var cancellationToken = context.CancellationToken; cancellationToken.ThrowIfCancellationRequested(); var root = await document.GetSyntaxRootAsync(cancellationToken); var classDeclaration = root.FindToken(span.Start).Parent?.FirstAncestorOrSelf<ClassDeclarationSyntax>(); if (classDeclaration != null) { var title = string.Format(FxCopRulesResources.StaticHolderTypeIsNotStatic, classDeclaration.Identifier.Text); var codeAction = new MyCodeAction(title, ct => AddStaticKeyword(document, root, classDeclaration)); context.RegisterCodeFix(codeAction, context.Diagnostics); } }
public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) { var document = context.Document; var span = context.Span; var cancellationToken = context.CancellationToken; cancellationToken.ThrowIfCancellationRequested(); var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); var classDeclaration = root.FindToken(span.Start).Parent?.FirstAncestorOrSelf<ClassDeclarationSyntax>(); if (classDeclaration != null) { var codeAction = new MyCodeAction(MicrosoftApiDesignGuidelinesAnalyzersResources.MakeClassStatic, async ct => await MakeClassStatic(document, root, classDeclaration, ct).ConfigureAwait(false)); context.RegisterCodeFix(codeAction, context.Diagnostics); } }
public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) { var document = context.Document; var span = context.Span; var diagnostics = context.Diagnostics; var cancellationToken = context.CancellationToken; var project = document.Project; var diagnostic = diagnostics.First(); var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); var node = root.FindToken(span.Start).GetAncestors<SyntaxNode>().First(n => n.Span.Contains(span)); using (Logger.LogBlock(FunctionId.Refactoring_FullyQualify, cancellationToken)) { // Has to be a simple identifier or generic name. if (node != null && CanFullyQualify(diagnostic, ref node)) { var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); var matchingTypes = await this.GetMatchingTypesAsync(project, semanticModel, node, cancellationToken).ConfigureAwait(false); var matchingNamespaces = await this.GetMatchingNamespacesAsync(project, semanticModel, node, cancellationToken).ConfigureAwait(false); if (matchingTypes != null || matchingNamespaces != null) { matchingTypes = matchingTypes ?? SpecializedCollections.EmptyEnumerable<ISymbol>(); matchingNamespaces = matchingNamespaces ?? SpecializedCollections.EmptyEnumerable<ISymbol>(); var matchingTypeContainers = FilterAndSort(GetContainers(matchingTypes, semanticModel.Compilation)); var matchingNamespaceContainers = FilterAndSort(GetContainers(matchingNamespaces, semanticModel.Compilation)); var proposedContainers = matchingTypeContainers.Concat(matchingNamespaceContainers) .Distinct() .Take(MaxResults); var displayService = project.LanguageServices.GetService<ISymbolDisplayService>(); foreach (var container in proposedContainers) { var containerName = displayService.ToMinimalDisplayString(semanticModel, node.SpanStart, container); var syntaxFacts = document.Project.LanguageServices.GetService<ISyntaxFactsService>(); string name; int arity; syntaxFacts.GetNameAndArityOfSimpleName(node, out name, out arity); // Actual member name might differ by case. string memberName; if (this.IgnoreCase) { var member = container.GetMembers(name).FirstOrDefault(); memberName = member != null ? member.Name : name; } else { memberName = name; } var codeAction = new MyCodeAction( $"{containerName}.{memberName}", c => ProcessNode(document, node, containerName, c)); context.RegisterCodeFix(codeAction, diagnostic); } } } } }
public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) { var document = context.Document; var span = context.Span; var diagnostics = context.Diagnostics; var cancellationToken = context.CancellationToken; var project = document.Project; var diagnostic = diagnostics.First(); var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); var ancestors = root.FindToken(span.Start, findInsideTrivia: true).GetAncestors <SyntaxNode>(); if (!ancestors.Any()) { return; } var node = ancestors.FirstOrDefault(n => n.Span.Contains(span) && n != root); if (node == null) { return; } var placeSystemNamespaceFirst = document.Project.Solution.Workspace.Options.GetOption( OrganizerOptions.PlaceSystemNamespaceFirst, document.Project.Language); using (Logger.LogBlock(FunctionId.Refactoring_AddImport, cancellationToken)) { if (!cancellationToken.IsCancellationRequested) { if (this.CanAddImport(node, cancellationToken)) { var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); var allSymbolReferences = new List <SymbolReference>(); var finder = new SymbolReferenceFinder(this, document, semanticModel, diagnostic, node, cancellationToken); // Caches so we don't produce the same data multiple times while searching // all over the solution. var projectToAssembly = new ConcurrentDictionary <Project, AsyncLazy <IAssemblySymbol> >(concurrencyLevel: 2, capacity: project.Solution.ProjectIds.Count); var referenceToCompilation = new ConcurrentDictionary <PortableExecutableReference, Compilation>(concurrencyLevel: 2, capacity: project.Solution.Projects.Sum(p => p.MetadataReferences.Count)); // Look for exact matches first: await FindResults(projectToAssembly, referenceToCompilation, project, allSymbolReferences, finder, exact : true, cancellationToken : cancellationToken).ConfigureAwait(false); if (allSymbolReferences.Count == 0) { // No exact matches found. Fall back to fuzzy searching. await FindResults(projectToAssembly, referenceToCompilation, project, allSymbolReferences, finder, exact : false, cancellationToken : cancellationToken).ConfigureAwait(false); } // Nothing found at all. No need to proceed. if (allSymbolReferences.Count == 0) { return; } cancellationToken.ThrowIfCancellationRequested(); foreach (var reference in allSymbolReferences) { var description = this.GetDescription(reference.SearchResult.Symbol, semanticModel, node); if (description != null) { var action = new MyCodeAction(description, c => this.AddImportAndReferenceAsync(node, reference, document, placeSystemNamespaceFirst, c)); context.RegisterCodeFix(action, diagnostic); } } } } } }
public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) { var document = context.Document; var span = context.Span; var diagnostics = context.Diagnostics; var cancellationToken = context.CancellationToken; var project = document.Project; var diagnostic = diagnostics.First(); var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); var ancestors = root.FindToken(span.Start, findInsideTrivia: true).GetAncestors <SyntaxNode>(); if (!ancestors.Any()) { return; } var node = ancestors.FirstOrDefault(n => n.Span.Contains(span) && n != root); if (node == null) { return; } var placeSystemNamespaceFirst = document.Project.Solution.Workspace.Options.GetOption(Microsoft.CodeAnalysis.Shared.Options.OrganizerOptions.PlaceSystemNamespaceFirst, document.Project.Language); using (Logger.LogBlock(FunctionId.Refactoring_AddImport, cancellationToken)) { if (!cancellationToken.IsCancellationRequested) { if (this.CanAddImport(node, cancellationToken)) { var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); var containingType = semanticModel.GetEnclosingNamedType(node.SpanStart, cancellationToken); var containingTypeOrAssembly = containingType ?? (ISymbol)semanticModel.Compilation.Assembly; var namespacesInScope = this.GetNamespacesInScope(semanticModel, node, cancellationToken); var syntaxFacts = document.Project.LanguageServices.GetService <ISyntaxFactsService>(); var matchingTypesNamespaces = await this.GetNamespacesForMatchingTypesAsync(project, diagnostic, node, semanticModel, namespacesInScope, syntaxFacts, cancellationToken).ConfigureAwait(false); var matchingTypes = await this.GetMatchingTypesAsync(project, diagnostic, node, semanticModel, namespacesInScope, syntaxFacts, cancellationToken).ConfigureAwait(false); var matchingNamespaces = await this.GetNamespacesForMatchingNamespacesAsync(project, diagnostic, node, semanticModel, namespacesInScope, syntaxFacts, cancellationToken).ConfigureAwait(false); var matchingExtensionMethodsNamespaces = await this.GetNamespacesForMatchingExtensionMethodsAsync(project, diagnostic, node, semanticModel, namespacesInScope, syntaxFacts, cancellationToken).ConfigureAwait(false); var matchingFieldsAndPropertiesAsync = await this.GetNamespacesForMatchingFieldsAndPropertiesAsync(project, diagnostic, node, semanticModel, namespacesInScope, syntaxFacts, cancellationToken).ConfigureAwait(false); var queryPatternsNamespaces = await this.GetNamespacesForQueryPatternsAsync(project, diagnostic, node, semanticModel, namespacesInScope, cancellationToken).ConfigureAwait(false); if (matchingTypesNamespaces != null || matchingNamespaces != null || matchingExtensionMethodsNamespaces != null || matchingFieldsAndPropertiesAsync != null || queryPatternsNamespaces != null || matchingTypes != null) { matchingTypesNamespaces = matchingTypesNamespaces ?? SpecializedCollections.EmptyList <INamespaceSymbol>(); matchingNamespaces = matchingNamespaces ?? SpecializedCollections.EmptyList <INamespaceSymbol>(); matchingExtensionMethodsNamespaces = matchingExtensionMethodsNamespaces ?? SpecializedCollections.EmptyList <INamespaceSymbol>(); matchingFieldsAndPropertiesAsync = matchingFieldsAndPropertiesAsync ?? SpecializedCollections.EmptyList <INamespaceSymbol>(); queryPatternsNamespaces = queryPatternsNamespaces ?? SpecializedCollections.EmptyList <INamespaceSymbol>(); matchingTypes = matchingTypes ?? SpecializedCollections.EmptyList <ITypeSymbol>(); var proposedImports = matchingTypesNamespaces.Cast <INamespaceOrTypeSymbol>() .Concat(matchingNamespaces.Cast <INamespaceOrTypeSymbol>()) .Concat(matchingExtensionMethodsNamespaces.Cast <INamespaceOrTypeSymbol>()) .Concat(matchingFieldsAndPropertiesAsync.Cast <INamespaceOrTypeSymbol>()) .Concat(queryPatternsNamespaces.Cast <INamespaceOrTypeSymbol>()) .Concat(matchingTypes.Cast <INamespaceOrTypeSymbol>()) .Distinct() .Where(NotNull) .Where(NotGlobalNamespace) .OrderBy(INamespaceOrTypeSymbolExtensions.CompareNamespaceOrTypeSymbols) .Take(8) .ToList(); if (proposedImports.Count > 0) { cancellationToken.ThrowIfCancellationRequested(); foreach (var import in proposedImports) { var description = this.GetDescription(import, semanticModel, node); if (description != null) { var action = new MyCodeAction(description, (c) => this.AddImportAsync(node, import, document, placeSystemNamespaceFirst, cancellationToken)); context.RegisterCodeFix(action, diagnostic); } } } } } } } }
public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) { var document = context.Document; var span = context.Span; var diagnostics = context.Diagnostics; var cancellationToken = context.CancellationToken; var project = document.Project; var diagnostic = diagnostics.First(); var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); var node = root.FindToken(span.Start).GetAncestors <SyntaxNode>().First(n => n.Span.Contains(span)); using (Logger.LogBlock(FunctionId.Refactoring_FullyQualify, cancellationToken)) { // Has to be a simple identifier or generic name. if (node != null && CanFullyQualify(diagnostic, ref node)) { var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); var matchingTypes = await this.GetMatchingTypesAsync(project, semanticModel, node, cancellationToken).ConfigureAwait(false); var matchingNamespaces = await this.GetMatchingNamespacesAsync(project, semanticModel, node, cancellationToken).ConfigureAwait(false); if (matchingTypes != null || matchingNamespaces != null) { matchingTypes = matchingTypes ?? SpecializedCollections.EmptyEnumerable <SymbolResult <ISymbol> >(); matchingNamespaces = matchingNamespaces ?? SpecializedCollections.EmptyEnumerable <SymbolResult <ISymbol> >(); var matchingTypeContainers = FilterAndSort(GetContainers(matchingTypes, semanticModel.Compilation)); var matchingNamespaceContainers = FilterAndSort(GetContainers(matchingNamespaces, semanticModel.Compilation)); var proposedContainers = matchingTypeContainers.Concat(matchingNamespaceContainers) .Distinct() .Take(MaxResults); var displayService = project.LanguageServices.GetService <ISymbolDisplayService>(); foreach (var container in proposedContainers) { var containerName = displayService.ToMinimalDisplayString(semanticModel, node.SpanStart, container); var syntaxFacts = document.Project.LanguageServices.GetService <ISyntaxFactsService>(); string name; int arity; syntaxFacts.GetNameAndArityOfSimpleName(node, out name, out arity); // Actual member name might differ by case. string memberName; if (this.IgnoreCase) { var member = container.GetMembers(name).FirstOrDefault(); memberName = member != null ? member.Name : name; } else { memberName = name; } var codeAction = new MyCodeAction( $"{containerName}.{memberName}", c => ProcessNode(document, node, containerName, c)); context.RegisterCodeFix(codeAction, diagnostic); } } } } }
public sealed override async Task ComputeRefactoringsAsync(CodeRefactoringContext context) { var(container, explicitName, name) = await GetContainerAsync(context).ConfigureAwait(false); if (container == null) { return; } if (!CheckExplicitNameAllowsConversion(explicitName)) { return; } var(document, _, cancellationToken) = context; var semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); var member = semanticModel.GetDeclaredSymbol(container, cancellationToken); Contract.ThrowIfNull(member); if (!CheckMemberCanBeConverted(member)) { return; } var project = document.Project; var directlyImplementedMembers = new MemberImplementationMap(); // Grab the name off of the *interface* member being implemented, not the implementation // member. Interface member names are the expected names that people expect to see // (like "GetEnumerator"), instead of the auto-generated names that the compiler makes // like: "System.IEnumerable.GetEnumerator" directlyImplementedMembers.AddRange(member, member.ExplicitOrImplicitInterfaceImplementations()); var codeAction = new MyCodeAction( string.Format(Implement_0, member.ExplicitOrImplicitInterfaceImplementations().First().Name), c => ChangeImplementationAsync(project, directlyImplementedMembers, c)); var containingType = member.ContainingType; var interfaceTypes = directlyImplementedMembers.SelectMany(kvp => kvp.Value).Select( s => s.ContainingType).Distinct().ToImmutableArray(); var implementedMembersFromSameInterfaces = GetImplementedMembers(containingType, interfaceTypes); var implementedMembersFromAllInterfaces = GetImplementedMembers(containingType, containingType.AllInterfaces); var offerForSameInterface = TotalCount(implementedMembersFromSameInterfaces) > TotalCount(directlyImplementedMembers); var offerForAllInterfaces = TotalCount(implementedMembersFromAllInterfaces) > TotalCount(implementedMembersFromSameInterfaces); // If there's only one member in the interface we implement, and there are no other // interfaces, then just offer to switch the implementation for this single member if (!offerForSameInterface && !offerForAllInterfaces) { context.RegisterRefactoring(codeAction); return; } // Otherwise, create a top level action to change the implementation, and offer this // action, along with either/both of the other two. var nestedActions = ArrayBuilder <CodeAction> .GetInstance(); nestedActions.Add(codeAction); if (offerForSameInterface) { var interfaceNames = interfaceTypes.Select(i => i.ToDisplayString(NameAndTypeParametersFormat)); nestedActions.Add(new MyCodeAction( string.Format(Implement_0, string.Join(", ", interfaceNames)), c => ChangeImplementationAsync(project, implementedMembersFromSameInterfaces, c))); } if (offerForAllInterfaces) { nestedActions.Add(new MyCodeAction( Implement_all_interfaces, c => ChangeImplementationAsync(project, implementedMembersFromAllInterfaces, c))); } context.RegisterRefactoring(new CodeAction.CodeActionWithNestedActions( Implement, nestedActions.ToImmutableAndFree(), isInlinable: true)); }
private static void RegisterFixForMethodOverloads( CodeFixContext context, SeparatedSyntaxList <TArgumentSyntax> arguments, ImmutableArray <ArgumentInsertPositionData <TArgumentSyntax> > methodsAndArgumentsToAdd) { var codeFixData = PrepareCreationOfCodeActions(context.Document, arguments, methodsAndArgumentsToAdd); // To keep the list of offered fixes short we create one menu entry per overload only // as long as there are two or less overloads present. If there are more overloads we // create two menu entries. One entry for non-cascading fixes and one with cascading fixes. var fixes = codeFixData.Length <= 2 ? NestByOverload() : NestByCascading(); context.RegisterFixes(fixes, context.Diagnostics); return; ImmutableArray <CodeAction> NestByOverload() { using var builderDisposer = ArrayBuilder <CodeAction> .GetInstance(codeFixData.Length, out var builder); foreach (var data in codeFixData) { // We create the mandatory data.CreateChangedSolutionNonCascading fix first. var title = GetCodeFixTitle(FeaturesResources.Add_parameter_to_0, data.Method, includeParameters: true); CodeAction codeAction = new MyCodeAction( title: title, data.CreateChangedSolutionNonCascading); if (data.CreateChangedSolutionCascading != null) { // We have two fixes to offer. We nest the two fixes in an inlinable CodeAction // so the IDE is free to either show both at once or to create a sub-menu. var titleForNesting = GetCodeFixTitle(FeaturesResources.Add_parameter_to_0, data.Method, includeParameters: true); var titleCascading = GetCodeFixTitle(FeaturesResources.Add_parameter_to_0_and_overrides_implementations, data.Method, includeParameters: true); codeAction = new CodeAction.CodeActionWithNestedActions( title: titleForNesting, ImmutableArray.Create( codeAction, new MyCodeAction( title: titleCascading, data.CreateChangedSolutionCascading)), isInlinable: true); } // codeAction is now either a single fix or two fixes wrapped in a CodeActionWithNestedActions builder.Add(codeAction); } return(builder.ToImmutable()); } ImmutableArray <CodeAction> NestByCascading() { using var builderDisposer = ArrayBuilder <CodeAction> .GetInstance(capacity : 2, out var builder); var nonCascadingActions = codeFixData.SelectAsArray(data => { var title = GetCodeFixTitle(FeaturesResources.Add_to_0, data.Method, includeParameters: true); return((CodeAction) new MyCodeAction(title: title, data.CreateChangedSolutionNonCascading)); }); var cascadingActions = codeFixData.SelectAsArray( data => data.CreateChangedSolutionCascading != null, data => { var title = GetCodeFixTitle(FeaturesResources.Add_to_0, data.Method, includeParameters: true); return((CodeAction) new MyCodeAction(title: title, data.CreateChangedSolutionCascading !)); });
private IEnumerable<CodeAction> CreateActions( CodeFixContext context, Document document, Diagnostic diagnostic, SyntaxNode node, SemanticModel semanticModel, IEnumerable<INamespaceOrTypeSymbol> proposedContainers, ISymbolDisplayService displayService) { foreach (var container in proposedContainers) { var containerName = displayService.ToMinimalDisplayString(semanticModel, node.SpanStart, container); var name = GetNodeName(document, node); // Actual member name might differ by case. string memberName; if (this.IgnoreCase) { var member = container.GetMembers(name).FirstOrDefault(); memberName = member != null ? member.Name : name; } else { memberName = name; } var codeAction = new MyCodeAction( $"{containerName}.{memberName}", c => ProcessNode(document, node, containerName, c)); yield return codeAction; } }
private void RegisterFixForMethodOverloads( CodeFixContext context, SeparatedSyntaxList <TArgumentSyntax> arguments, ImmutableArray <ArgumentInsertPositionData <TArgumentSyntax> > methodsAndArgumentsToAdd) { var codeFixData = PrepareCreationOfCodeActions(context.Document, arguments, methodsAndArgumentsToAdd); // To keep the list of offered fixes short we create one menu entry per overload only // as long as there are two or less overloads present. If there are more overloads we // create two menu entries. One entry for non-cascading fixes and one with cascading fixes. var fixes = codeFixData.Length <= 2 ? NestByOverload() : NestByCascading(); context.RegisterFixes(fixes, context.Diagnostics); return; ImmutableArray <CodeAction> NestByOverload() { var builder = ArrayBuilder <CodeAction> .GetInstance(codeFixData.Length); foreach (var data in codeFixData) { // We create the mandatory data.CreateChangedSolutionNonCascading fix first. var title = GetCodeFixTitle(FeaturesResources.Add_parameter_to_0, data.Method, includeParameters: true); CodeAction codeAction = new MyCodeAction( title: title, data.CreateChangedSolutionNonCascading); if (data.CreateChangedSolutionCascading != null) { // We have two fixes to offer. We nest the two fixes in an inlinable CodeAction // so the IDE is free to either show both at once or to create a sub-menu. var titleForNesting = GetCodeFixTitle(FeaturesResources.Add_parameter_to_0, data.Method, includeParameters: true); var titleCascading = GetCodeFixTitle(FeaturesResources.Add_parameter_to_0_and_overrides_implementations, data.Method, includeParameters: true); codeAction = new CodeAction.CodeActionWithNestedActions( title: titleForNesting, ImmutableArray.Create( codeAction, new MyCodeAction( title: titleCascading, data.CreateChangedSolutionCascading)), isInlinable: true); } // codeAction is now either a single fix or two fixes wrapped in a CodeActionWithNestedActions builder.Add(codeAction); } return(builder.ToImmutableAndFree()); } ImmutableArray <CodeAction> NestByCascading() { var builder = ArrayBuilder <CodeAction> .GetInstance(2); var nonCascadingActions = ImmutableArray.CreateRange <CodeFixData, CodeAction>(codeFixData, data => { var title = GetCodeFixTitle(FeaturesResources.Add_to_0, data.Method, includeParameters: true); return(new MyCodeAction(title: title, data.CreateChangedSolutionNonCascading)); }); var cascading = codeFixData.Where(data => data.CreateChangedSolutionCascading != null); var cascadingActions = ImmutableArray.CreateRange <CodeAction>(cascading.Select(data => { var title = GetCodeFixTitle(FeaturesResources.Add_to_0, data.Method, includeParameters: true); return(new MyCodeAction(title: title, data.CreateChangedSolutionCascading)); })); var aMethod = codeFixData.First().Method; // We need to term the MethodGroup and need an arbitrary IMethodSymbol to do so. var nestedNonCascadingTitle = GetCodeFixTitle(FeaturesResources.Add_parameter_to_0, aMethod, includeParameters: false); // Create a sub-menu entry with all the non-cascading CodeActions. // We make sure the IDE does not inline. Otherwise the context menu gets flooded with our fixes. builder.Add(new CodeAction.CodeActionWithNestedActions(nestedNonCascadingTitle, nonCascadingActions, isInlinable: false)); if (cascadingActions.Length > 0) { // if there are cascading CodeActions create a second sub-menu. var nestedCascadingTitle = GetCodeFixTitle(FeaturesResources.Add_parameter_to_0_and_overrides_implementations, aMethod, includeParameters: false); builder.Add(new CodeAction.CodeActionWithNestedActions(nestedCascadingTitle, cascadingActions, isInlinable: false)); } return(builder.ToImmutableAndFree()); } }
public sealed override Task ComputeRefactoringsAsync(CodeRefactoringContext context) { var codeAction = new MyCodeAction(context.Document); context.RegisterRefactoring(codeAction); return SpecializedTasks.EmptyTask; }
public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) { var document = context.Document; var span = context.Span; var diagnostics = context.Diagnostics; var cancellationToken = context.CancellationToken; var project = document.Project; var diagnostic = diagnostics.First(); var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); var ancestors = root.FindToken(span.Start, findInsideTrivia: true).GetAncestors<SyntaxNode>(); if (!ancestors.Any()) { return; } var node = ancestors.FirstOrDefault(n => n.Span.Contains(span) && n != root); if (node == null) { return; } var placeSystemNamespaceFirst = document.Project.Solution.Workspace.Options.GetOption(Microsoft.CodeAnalysis.Shared.Options.OrganizerOptions.PlaceSystemNamespaceFirst, document.Project.Language); using (Logger.LogBlock(FunctionId.Refactoring_AddImport, cancellationToken)) { if (!cancellationToken.IsCancellationRequested) { if (this.CanAddImport(node, cancellationToken)) { var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); var containingType = semanticModel.GetEnclosingNamedType(node.SpanStart, cancellationToken); var containingTypeOrAssembly = containingType ?? (ISymbol)semanticModel.Compilation.Assembly; var namespacesInScope = this.GetNamespacesInScope(semanticModel, node, cancellationToken); var syntaxFacts = document.Project.LanguageServices.GetService<ISyntaxFactsService>(); var matchingTypesNamespaces = await this.GetNamespacesForMatchingTypesAsync(project, diagnostic, node, semanticModel, namespacesInScope, syntaxFacts, cancellationToken).ConfigureAwait(false); var matchingTypes = await this.GetMatchingTypesAsync(project, diagnostic, node, semanticModel, namespacesInScope, syntaxFacts, cancellationToken).ConfigureAwait(false); var matchingNamespaces = await this.GetNamespacesForMatchingNamespacesAsync(project, diagnostic, node, semanticModel, namespacesInScope, syntaxFacts, cancellationToken).ConfigureAwait(false); var matchingExtensionMethodsNamespaces = await this.GetNamespacesForMatchingExtensionMethodsAsync(project, diagnostic, node, semanticModel, namespacesInScope, syntaxFacts, cancellationToken).ConfigureAwait(false); var matchingFieldsAndPropertiesAsync = await this.GetNamespacesForMatchingFieldsAndPropertiesAsync(project, diagnostic, node, semanticModel, namespacesInScope, syntaxFacts, cancellationToken).ConfigureAwait(false); var queryPatternsNamespaces = await this.GetNamespacesForQueryPatternsAsync(project, diagnostic, node, semanticModel, namespacesInScope, cancellationToken).ConfigureAwait(false); if (matchingTypesNamespaces != null || matchingNamespaces != null || matchingExtensionMethodsNamespaces != null || matchingFieldsAndPropertiesAsync != null || queryPatternsNamespaces != null || matchingTypes != null) { matchingTypesNamespaces = matchingTypesNamespaces ?? SpecializedCollections.EmptyList<INamespaceSymbol>(); matchingNamespaces = matchingNamespaces ?? SpecializedCollections.EmptyList<INamespaceSymbol>(); matchingExtensionMethodsNamespaces = matchingExtensionMethodsNamespaces ?? SpecializedCollections.EmptyList<INamespaceSymbol>(); matchingFieldsAndPropertiesAsync = matchingFieldsAndPropertiesAsync ?? SpecializedCollections.EmptyList<INamespaceSymbol>(); queryPatternsNamespaces = queryPatternsNamespaces ?? SpecializedCollections.EmptyList<INamespaceSymbol>(); matchingTypes = matchingTypes ?? SpecializedCollections.EmptyList<ITypeSymbol>(); var proposedImports = matchingTypesNamespaces.Cast<INamespaceOrTypeSymbol>() .Concat(matchingNamespaces.Cast<INamespaceOrTypeSymbol>()) .Concat(matchingExtensionMethodsNamespaces.Cast<INamespaceOrTypeSymbol>()) .Concat(matchingFieldsAndPropertiesAsync.Cast<INamespaceOrTypeSymbol>()) .Concat(queryPatternsNamespaces.Cast<INamespaceOrTypeSymbol>()) .Concat(matchingTypes.Cast<INamespaceOrTypeSymbol>()) .Distinct() .Where(NotNull) .Where(NotGlobalNamespace) .OrderBy(INamespaceOrTypeSymbolExtensions.CompareNamespaceOrTypeSymbols) .Take(8) .ToList(); if (proposedImports.Count > 0) { cancellationToken.ThrowIfCancellationRequested(); foreach (var import in proposedImports) { var action = new MyCodeAction(this.GetDescription(import, semanticModel, node), (c) => this.AddImportAsync(node, import, document, placeSystemNamespaceFirst, cancellationToken)); context.RegisterCodeFix(action, diagnostic); } } } } } } }
protected override async Task<ImmutableArray<CodeAction>> GetRefactoringsForSingleParameterAsync( Document document, IParameterSymbol parameter, SyntaxNode functionDeclaration, IMethodSymbol method, IBlockOperation? blockStatementOpt, CancellationToken cancellationToken) { // Only supported for constructor parameters. if (method.MethodKind != MethodKind.Constructor) return ImmutableArray<CodeAction>.Empty; var typeDeclaration = functionDeclaration.GetAncestor<TTypeDeclarationSyntax>(); if (typeDeclaration == null) return ImmutableArray<CodeAction>.Empty; var assignmentStatement = TryFindFieldOrPropertyAssignmentStatement( parameter, blockStatementOpt); if (assignmentStatement != null) { // We're already assigning this parameter to a field/property in this type. // So there's nothing more for us to do. return ImmutableArray<CodeAction>.Empty; } // Haven't initialized any fields/properties with this parameter. Offer to assign // to an existing matching field/prop if we can find one, or add a new field/prop // if we can't. var rules = await document.GetNamingRulesAsync(cancellationToken).ConfigureAwait(false); var parameterNameParts = IdentifierNameParts.CreateIdentifierNameParts(parameter, rules); if (parameterNameParts.BaseName == "") { return ImmutableArray<CodeAction>.Empty; } var fieldOrProperty = await TryFindMatchingUninitializedFieldOrPropertySymbolAsync( document, parameter, blockStatementOpt, rules, parameterNameParts.BaseNameParts, cancellationToken).ConfigureAwait(false); if (fieldOrProperty != null) { // Found a field/property that this parameter should be assigned to. // Just offer the simple assignment to it. var resource = fieldOrProperty.Kind == SymbolKind.Field ? FeaturesResources.Initialize_field_0 : FeaturesResources.Initialize_property_0; var title = string.Format(resource, fieldOrProperty.Name); return ImmutableArray.Create<CodeAction>(new MyCodeAction( title, c => AddSymbolInitializationAsync( document, parameter, functionDeclaration, blockStatementOpt, fieldOrProperty, c))); } else { // Didn't find a field/prop that this parameter could be assigned to. // Offer to create new one and assign to that. var codeGenService = document.GetLanguageService<ICodeGenerationService>(); var options = await document.GetOptionsAsync(cancellationToken).ConfigureAwait(false); var requireAccessibilityModifiers = options.GetOption(CodeStyleOptions2.RequireAccessibilityModifiers); var field = CreateField(requireAccessibilityModifiers, parameter, rules, parameterNameParts.BaseNameParts); var property = CreateProperty(requireAccessibilityModifiers, parameter, rules, parameterNameParts.BaseNameParts); var fieldAction = new MyCodeAction(string.Format(FeaturesResources.Create_and_initialize_field_0, field.Name), c => AddSymbolInitializationAsync(document, parameter, functionDeclaration, blockStatementOpt, field, c)); var propertyAction = new MyCodeAction(string.Format(FeaturesResources.Create_and_initialize_property_0, property.Name), c => AddSymbolInitializationAsync(document, parameter, functionDeclaration, blockStatementOpt, property, c)); // Check if the surrounding parameters are assigned to another field in this class. If so, offer to // make this parameter into a field as well. Otherwise, default to generating a property var siblingFieldOrProperty = TryFindSiblingFieldOrProperty(parameter, blockStatementOpt); return siblingFieldOrProperty is IFieldSymbol ? ImmutableArray.Create<CodeAction>(fieldAction, propertyAction) : ImmutableArray.Create<CodeAction>(propertyAction, fieldAction); } }