public override async Task ComputeRefactoringsAsync(CodeRefactoringContext context) { // Don't bother if there isn't a selection var textSpan = context.Span; if (textSpan.IsEmpty) { return; } var document = context.Document; var cancellationToken = context.CancellationToken; var workspace = document.Project.Solution.Workspace; if (workspace.Kind == WorkspaceKind.MiscellaneousFiles) { return; } var activeInlineRenameSession = workspace.Services.GetService<ICodeRefactoringHelpersService>().ActiveInlineRenameSession; if (activeInlineRenameSession) { return; } if (cancellationToken.IsCancellationRequested) { return; } var action = await GetCodeActionAsync(document, textSpan, cancellationToken: cancellationToken).ConfigureAwait(false); if (action == null) { return; } context.RegisterRefactoring(action.Item1); }
private static void RenameIdentifierNameAccordingToTypeName( CodeRefactoringContext context, SemanticModel semanticModel, ForEachStatementSyntax forEachStatement) { if (forEachStatement.Type == null) { return; } if (!forEachStatement.Identifier.Span.Contains(context.Span)) { return; } string newName = NamingHelper.CreateIdentifierName( forEachStatement.Type, semanticModel, firstCharToLower: true); if (string.IsNullOrEmpty(newName)) { return; } if (string.Equals(newName, forEachStatement.Identifier.ValueText, StringComparison.Ordinal)) { return; } ISymbol symbol = semanticModel.GetDeclaredSymbol(forEachStatement, context.CancellationToken); context.RegisterRefactoring( $"Rename foreach variable to '{newName}'", cancellationToken => symbol.RenameAsync(newName, context.Document, cancellationToken)); }
public sealed override async Task ComputeRefactoringsAsync(CodeRefactoringContext context) { var document = context.Document; var textSpan = context.Span; var cancellationToken = context.CancellationToken; var root = (SyntaxNode)await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); var token = root.FindToken(textSpan.Start); // Only trigger if the text span is within the 'if' keyword token of an if-else statement. if (token.Kind() != SyntaxKind.IfKeyword || !token.Span.IntersectsWith(textSpan.Start) || !token.Span.IntersectsWith(textSpan.End)) { return; } var ifStatement = token.Parent as IfStatementSyntax; if (ifStatement == null || ifStatement.Else == null) { return; } var semanticModel = (SemanticModel)await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); ReturnStatementSyntax returnStatement; if (ReturnConditionalAnalyzer.TryGetNewReturnStatement(ifStatement, semanticModel, out returnStatement)) { var action = new ConvertToConditionalCodeAction("Convert to conditional expression", (c) => Task.FromResult(ConvertToConditional(document, semanticModel, ifStatement, returnStatement, c))); context.RegisterRefactoring(action); } }
public sealed override async Task ComputeRefactoringsAsync(CodeRefactoringContext context) { var expressionStatement = await GetExpressionStatementAsync(context) .ConfigureAwait(false); if (expressionStatement == null) { return; } var(document, _, cancellationToken) = context; var syntaxFacts = document.GetLanguageService <ISyntaxFactsService>(); var expression = syntaxFacts.GetExpressionOfExpressionStatement(expressionStatement); var semanticModel = await document .GetSemanticModelAsync(cancellationToken) .ConfigureAwait(false); var type = semanticModel.GetTypeInfo(expression).Type; if (type == null || type.SpecialType == SpecialType.System_Void) { return; } var singleLineExpression = syntaxFacts.ConvertToSingleLine(expression); var nodeString = singleLineExpression.ToString(); context.RegisterRefactoring( new MyCodeAction( string.Format(FeaturesResources.Introduce_local_for_0, nodeString), c => IntroduceLocalAsync(document, expressionStatement, c) ), expressionStatement.Span ); }
public override async Task ComputeRefactoringsAsync(CodeRefactoringContext context) { var(document, textSpan, cancellationToken) = context; if (document.Project.Solution.WorkspaceKind == WorkspaceKind.MiscellaneousFiles) { return; } if (textSpan.Length > 0) { return; } var root = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); if (root.SyntaxTree.Options.LanguageVersion() < LanguageVersion.CSharp9) { return; } var model = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); var node = root.FindToken(textSpan.Start).Parent; var replacementFunc = GetReplacementFunc(node, model); if (replacementFunc is null) { return; } context.RegisterRefactoring( CodeAction.Create( CSharpFeaturesResources.Use_recursive_patterns, _ => Task.FromResult(document.WithSyntaxRoot(replacementFunc(root))), nameof(CSharpFeaturesResources.Use_recursive_patterns))); }
ComputeRefactoringsAsync(CodeRefactoringContext context) { var root = await context.Document.GetSyntaxRootAsync(context. CancellationToken). ConfigureAwait(false); // Find the node at the selection. var node = root.FindNode(context.Span); // Only offer a refactoring if the selected node is // a class statement node. var classDecl = node as ClassDeclarationSyntax; if (classDecl == null) { return; } var action = CodeAction.Create(title: Title, createChangedDocument: c => MakeRelayCommandAsync(context.Document, classDecl, c), equivalenceKey: Title); // Register this code action. context.RegisterRefactoring(action); }
public sealed override async Task ComputeRefactoringsAsync(CodeRefactoringContext context) { Document document = context.Document; TextSpan textSpan = context.Span; CancellationToken cancellationToken = context.CancellationToken; // shouldn't have selection if (!textSpan.IsEmpty) { return; } // get applicable actions ApplicableActionFinder finder = new ApplicableActionFinder(document, textSpan.Start, cancellationToken); (TextSpan span, CodeAction action) = await finder.GetSpanAndActionAsync().ConfigureAwait(false); if (action == null || !span.IntersectsWith(textSpan.Start)) { return; } context.RegisterRefactoring(action); }
private static void CreateField(CodeRefactoringContext context, Document document, TextSpan span, SemanticModel model, SyntaxNode root, LiteralExpressionSyntax constantLiteral, SyntaxNode parentBlock, MemberDeclarationSyntax parentTypeMember) { context.RegisterRefactoring( CodeActionFactory.Create( span, DiagnosticSeverity.Info, GettextCatalog.GetString("Create constant field"), t2 => { TypeInfo constType = model.GetTypeInfo(constantLiteral); string newConstName = CreateName(context, root, parentBlock, constType.ConvertedType.Name); var newConstDecl = SyntaxFactory.FieldDeclaration(SyntaxFactory.List <AttributeListSyntax>(), CreateConst(), CreateVariableDecl(constantLiteral.Token, model, constType, newConstName) ).WithAdditionalAnnotations(Formatter.Annotation); var trackedRoot = root.TrackNodes(constantLiteral, parentTypeMember); var newRoot = trackedRoot.InsertNodesBefore(trackedRoot.GetCurrentNode(parentTypeMember), new[] { newConstDecl }); newRoot = ReplaceWithConst(newRoot.GetCurrentNode(constantLiteral), newConstName, newRoot); return(Task.FromResult(document.WithSyntaxRoot(newRoot))); }) ); }
public sealed override async Task ComputeRefactoringsAsync(CodeRefactoringContext context) { // TODO: Replace the following code with your own analysis, generating a CodeAction for each refactoring to offer var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); // Find the node at the selection. var node = root.FindNode(context.Span); // Only offer a refactoring if the selected node is a type declaration node. if (!(node is ParameterSyntax parameterSyntax)) { return; } var parameterNode = (ParameterSyntax)node; var underscoreName = $"_{parameterNode.Identifier}"; // For any type declaration node, create a code action to reverse the identifier text. var action = CodeAction.Create($"Create and initialize field {underscoreName}", c => CreateInitilizeFieldAsync(context.Document, parameterSyntax, underscoreName, c)); // Register this code action. context.RegisterRefactoring(action); }
public override async Task ComputeRefactoringsAsync(CodeRefactoringContext context) { var document = context.Document; if (document.Project.Solution.Workspace.Kind == WorkspaceKind.MiscellaneousFiles) { return; } var span = context.Span; if (!span.IsEmpty) { return; } var cancellationToken = context.CancellationToken; if (cancellationToken.IsCancellationRequested) { return; } var model = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); if (model.IsFromGeneratedCode(cancellationToken)) { return; } var root = await model.SyntaxTree.GetRootAsync(cancellationToken).ConfigureAwait(false); var token = root.FindToken(span.Start); if (!token.IsKind(SyntaxKind.IdentifierToken)) { return; } var node = token.Parent as VariableDeclaratorSyntax; if (node == null) { return; } var declaredSymbol = model.GetDeclaredSymbol(node, cancellationToken); if (declaredSymbol == null || !declaredSymbol.IsKind(SymbolKind.Event)) { return; } if (declaredSymbol.ContainingSymbol.IsInterfaceType()) { return; } var invokeMethod = declaredSymbol.GetReturnType().GetDelegateInvokeMethod(); if (invokeMethod == null) { return; } context.RegisterRefactoring( CodeActionFactory.CreateInsertion( span, DiagnosticSeverity.Info, GettextCatalog.GetString("Create event invocator"), t2 => { SyntaxNode eventInvocator = CreateEventInvocator(document, declaredSymbol); return(Task.FromResult(new InsertionResult(context, eventInvocator, declaredSymbol.ContainingType, declaredSymbol.Locations.First()))); } ) ); }
public override async Task ComputeRefactoringsAsync(CodeRefactoringContext context) { var document = context.Document; if (document.Project.Solution.Workspace.Kind == WorkspaceKind.MiscellaneousFiles) { return; } var span = context.Span; if (!span.IsEmpty) { return; } var cancellationToken = context.CancellationToken; if (cancellationToken.IsCancellationRequested) { return; } var model = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); if (model.IsFromGeneratedCode(cancellationToken)) { return; } var root = await model.SyntaxTree.GetRootAsync(cancellationToken).ConfigureAwait(false); var node = root.FindNode(span) as IdentifierNameSyntax; if (node == null || !node.Parent.IsKind(SyntaxKind.SimpleMemberAccessExpression)) { return; } var memberAccess = node.Parent as MemberAccessExpressionSyntax; var invocation = node.Parent.Parent as InvocationExpressionSyntax; if (invocation == null) { return; } var invocationRR = model.GetSymbolInfo(invocation); if (invocationRR.Symbol == null) { return; } var method = invocationRR.Symbol as IMethodSymbol; if (method == null || !method.IsExtensionMethod) { return; } context.RegisterRefactoring( CodeActionFactory.Create( span, DiagnosticSeverity.Info, GettextCatalog.GetString("To static invocation"), t2 => { var newRoot = root.ReplaceNode((SyntaxNode)invocation, ToStaticMethodInvocation(model, invocation, memberAccess, invocationRR).WithAdditionalAnnotations(Formatter.Annotation).WithLeadingTrivia(invocation.GetLeadingTrivia())); return(Task.FromResult(document.WithSyntaxRoot(newRoot))); } ) ); }
private async Task HandleNonSelectionAsync(CodeRefactoringContext context) { var document = context.Document; var textSpan = context.Span; var cancellationToken = context.CancellationToken; var syntaxFacts = document.GetLanguageService <ISyntaxFactsService>(); var sourceText = await document.GetTextAsync(cancellationToken).ConfigureAwait(false); var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); // We offer the refactoring when the user is either on the header of a class/struct, // or if they're between any members of a class/struct and are on a blank line. if (!syntaxFacts.IsOnTypeHeader(root, textSpan.Start) && !syntaxFacts.IsBetweenTypeMembers(sourceText, root, textSpan.Start)) { return; } var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); // Only supported on classes/structs. var containingType = GetEnclosingNamedType(semanticModel, root, textSpan.Start, cancellationToken); if (containingType?.TypeKind != TypeKind.Class && containingType?.TypeKind != TypeKind.Struct) { return; } // No constructors for static classes. if (containingType.IsStatic) { return; } // Find all the possible writable instance fields/properties. If there are any, then // show a dialog to the user to select the ones they want. Otherwise, if there are none // don't offer to generate anything. var viableMembers = containingType.GetMembers().WhereAsArray(IsWritableInstanceFieldOrProperty); if (viableMembers.Length == 0) { return; } var pickMemberOptions = ArrayBuilder <PickMembersOption> .GetInstance(); var canAddNullCheck = viableMembers.Any( m => m.GetSymbolType().CanAddNullCheck()); if (canAddNullCheck) { var options = await document.GetOptionsAsync(cancellationToken).ConfigureAwait(false); var optionValue = options.GetOption(GenerateConstructorFromMembersOptions.AddNullChecks); pickMemberOptions.Add(new PickMembersOption( AddNullChecksId, FeaturesResources.Add_null_checks, optionValue)); } context.RegisterRefactoring( new GenerateConstructorWithDialogCodeAction( this, document, textSpan, containingType, viableMembers, pickMemberOptions.ToImmutableAndFree())); }
public override async Task ComputeRefactoringsAsync(CodeRefactoringContext context) { var document = context.Document; if (document.Project.Solution.Workspace.Kind == WorkspaceKind.MiscellaneousFiles) { return; } var span = context.Span; if (!span.IsEmpty) { return; } var cancellationToken = context.CancellationToken; if (cancellationToken.IsCancellationRequested) { return; } var model = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); if (model.IsFromGeneratedCode(cancellationToken)) { return; } var root = await model.SyntaxTree.GetRootAsync(cancellationToken).ConfigureAwait(false); var token = root.FindToken(span.Start); ExpressionSyntax identifier = token.Parent as IdentifierNameSyntax; if (identifier == null) { return; } // If identifier is a type name, this might be a static member access or similar, don't suggest null checks on it var identifierSymbol = model.GetSymbolInfo(identifier).Symbol; if ((identifierSymbol == null) || (identifierSymbol.IsType())) { return; } // Identifier might be part of a MemberAccessExpression and we need to check it for null as a whole identifier = GetOuterMemberAccessExpression(identifier) ?? identifier; if ((identifier.Parent is ExpressionSyntax) && ConditionContainsNullCheck((ExpressionSyntax)identifier.Parent, identifier)) { return; } var identifierAncestors = identifier.Ancestors(); // Don't surround Return statements with checks if (identifierAncestors.OfType <ReturnStatementSyntax>().Any()) { return; } // If identifier is in a conditional ternary expression, skip refactoring is case of present null check in its condition var conditionalExprParent = identifierAncestors.OfType <TernaryConditionalExpressionSyntax>().FirstOrDefault(); if ((conditionalExprParent != null) && ConditionContainsNullCheck(conditionalExprParent.Condition, identifier)) { return; } // Check identifier type, don't suggest null checks for value types! var identifierType = model.GetTypeInfo(identifier).Type; if ((identifierType == null) || (identifierType.IsValueType && !identifierType.IsNullableType())) { return; } SyntaxNode statementToWrap = identifierAncestors.OfType <ExecutableStatementSyntax>().FirstOrDefault(); if (statementToWrap == null) { return; } // No refactoring if statement is inside of a local variable declaration if (statementToWrap is LocalDeclarationStatementSyntax) { return; } bool wrapWithSingleLineIfStatement = false; SyntaxNode newWrappedStatement = null; var wrappedStatementAncestors = statementToWrap.Ancestors(); if (wrappedStatementAncestors.OfType <SingleLineLambdaExpressionSyntax>().Any()) { // Inside of a single-line lambda => wrap with single line If statement wrapWithSingleLineIfStatement = true; } // Check surrounding block var surroundingElseIfBlock = wrappedStatementAncestors.FirstOrDefault() as ElseIfBlockSyntax; if (surroundingElseIfBlock != null) { // Special handling for extension of Else If blocks if (StatementWithConditionContainsNullCheck(surroundingElseIfBlock, identifier)) { return; } statementToWrap = surroundingElseIfBlock; newWrappedStatement = ExtendIfConditionWithNullCheck(surroundingElseIfBlock, identifier); } else { var surroundingStatement = wrappedStatementAncestors.OfType <ExecutableStatementSyntax>().FirstOrDefault(); if (surroundingStatement != null) { if (StatementWithConditionContainsNullCheck(surroundingStatement, identifier)) { return; } if ((surroundingStatement is MultiLineIfBlockSyntax) || (surroundingStatement is SingleLineIfStatementSyntax)) { statementToWrap = surroundingStatement; newWrappedStatement = ExtendIfConditionWithNullCheck(surroundingStatement, identifier); } } else { if (StatementWithConditionContainsNullCheck(statementToWrap, identifier)) { return; } if ((statementToWrap is MultiLineIfBlockSyntax) || (statementToWrap is SingleLineIfStatementSyntax)) { newWrappedStatement = ExtendIfConditionWithNullCheck(statementToWrap, identifier); } } } if (newWrappedStatement == null) { if (wrapWithSingleLineIfStatement) { newWrappedStatement = SyntaxFactory.SingleLineIfStatement( SyntaxFactory.Token(SyntaxKind.IfKeyword), CreateIsNotNothingBinaryExpression(identifier), SyntaxFactory.Token(SyntaxKind.ThenKeyword), SyntaxFactory.List <StatementSyntax>(new[] { ((StatementSyntax)statementToWrap).WithoutLeadingTrivia().WithoutTrailingTrivia() }), null ).WithLeadingTrivia(statementToWrap.GetLeadingTrivia()).WithTrailingTrivia(statementToWrap.GetTrailingTrivia()).WithAdditionalAnnotations(Formatter.Annotation); } else { newWrappedStatement = SyntaxFactory.MultiLineIfBlock( SyntaxFactory.IfStatement( SyntaxFactory.Token(SyntaxKind.IfKeyword), CreateIsNotNothingBinaryExpression(identifier), SyntaxFactory.Token(SyntaxKind.ThenKeyword)), SyntaxFactory.List <StatementSyntax>(new[] { ((StatementSyntax)statementToWrap).WithoutLeadingTrivia().WithoutTrailingTrivia() }), SyntaxFactory.List <ElseIfBlockSyntax>(), null ).WithLeadingTrivia(statementToWrap.GetLeadingTrivia()).WithTrailingTrivia(statementToWrap.GetTrailingTrivia()).WithAdditionalAnnotations(Formatter.Annotation); } } context.RegisterRefactoring(CodeActionFactory.Create(token.Span, DiagnosticSeverity.Info, GettextCatalog.GetString("Add check for Nothing"), t2 => { var newRoot = root.ReplaceNode <SyntaxNode>(statementToWrap, newWrappedStatement); return(Task.FromResult(document.WithSyntaxRoot(newRoot))); })); }
public override async Task ComputeRefactoringsAsync(CodeRefactoringContext context) { var document = context.Document; if (document.Project.Solution.Workspace.Kind == WorkspaceKind.MiscellaneousFiles) { return; } var span = context.Span; if (!span.IsEmpty) { return; } var cancellationToken = context.CancellationToken; if (cancellationToken.IsCancellationRequested) { return; } var model = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); if (model.IsFromGeneratedCode(cancellationToken)) { return; } var root = await model.SyntaxTree.GetRootAsync(cancellationToken).ConfigureAwait(false); var token = root.FindToken(span.Start); if (token.Parent == null) { return; } var bracketedList = token.Parent.AncestorsAndSelf().OfType <BracketedArgumentListSyntax>().FirstOrDefault(); if (bracketedList == null) { return; } var elementAccess = bracketedList.AncestorsAndSelf().OfType <ElementAccessExpressionSyntax>().FirstOrDefault(); if (elementAccess == null) { return; } var elementType = model.GetTypeInfo(elementAccess.Expression); var type = elementType.Type; if (type == null) { return; } if (!IsDictionary(type as INamedTypeSymbol) && !type.AllInterfaces.Any(IsDictionary)) { return; } context.RegisterRefactoring( CodeActionFactory.Create( span, DiagnosticSeverity.Info, string.Format(GettextCatalog.GetString("Use 'if ({0}.TryGetValue({1}, out val))'"), elementAccess.Expression, elementAccess.ArgumentList.Arguments.First()), t2 => { var reservedNames = model.LookupSymbols(elementAccess.SpanStart).Select(s => s.Name); string localVariableName = NameGenerator.EnsureUniqueness("val", reservedNames, true); var parentStatement = elementAccess.Parent.AncestorsAndSelf().OfType <StatementSyntax>().FirstOrDefault(); var newParent = SyntaxFactory.IfStatement( SyntaxFactory.InvocationExpression( SyntaxFactory.MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, elementAccess.Expression, SyntaxFactory.IdentifierName("TryGetValue")), SyntaxFactory.ArgumentList(elementAccess.ArgumentList.Arguments.Add(SyntaxFactory.Argument(SyntaxFactory.IdentifierName(localVariableName)).WithRefOrOutKeyword(SyntaxFactory.Token(SyntaxKind.OutKeyword)))) ), parentStatement.ReplaceNode(elementAccess, SyntaxFactory.IdentifierName(localVariableName)) ).WithAdditionalAnnotations(Formatter.Annotation); var dict = IsDictionary(elementType.Type as INamedTypeSymbol) ? elementType.Type : elementType.Type.AllInterfaces.First(IsDictionary); var varDecl = SyntaxFactory.LocalDeclarationStatement( SyntaxFactory.VariableDeclaration( dict.GetTypeArguments()[1].GenerateTypeSyntax(), SyntaxFactory.SeparatedList(new[] { SyntaxFactory.VariableDeclarator(localVariableName) }) ) ).WithAdditionalAnnotations(Formatter.Annotation); SyntaxNode newRoot; if (parentStatement.Parent.IsKind(SyntaxKind.Block)) { newRoot = root.ReplaceNode(parentStatement, new SyntaxNode[] { varDecl, newParent }); } else { newRoot = root.ReplaceNode(parentStatement, SyntaxFactory.Block(varDecl, newParent).WithAdditionalAnnotations(Formatter.Annotation)); } return(Task.FromResult(document.WithSyntaxRoot(newRoot))); } ) ); }
public override async Task ComputeRefactoringsAsync(CodeRefactoringContext context) { var document = context.Document; if (document.Project.Solution.Workspace.Kind == WorkspaceKind.MiscellaneousFiles) { return; } var span = context.Span; if (!span.IsEmpty) { return; } var cancellationToken = context.CancellationToken; if (cancellationToken.IsCancellationRequested) { return; } var model = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); if (model.IsFromGeneratedCode(cancellationToken)) { return; } var root = await model.SyntaxTree.GetRootAsync(cancellationToken).ConfigureAwait(false); var token = root.FindToken(span.Start); if (token.Parent == null) { return; } var bracketedList = token.Parent.AncestorsAndSelf().OfType <BracketedArgumentListSyntax>().FirstOrDefault(); if (bracketedList == null) { return; } var elementAccess = bracketedList.AncestorsAndSelf().OfType <ElementAccessExpressionSyntax>().FirstOrDefault(); if (elementAccess == null) { return; } var elementType = model.GetTypeInfo(elementAccess.Expression); var type = elementType.Type; if (type == null) { return; } if (!IsCollection(type as INamedTypeSymbol) && !type.AllInterfaces.Any(IsCollection)) { return; } context.RegisterRefactoring( CodeActionFactory.Create( span, DiagnosticSeverity.Info, string.Format(GettextCatalog.GetString("Check 'if ({0}.Count > {1})'"), elementAccess.Expression, elementAccess.ArgumentList.Arguments.First()), t2 => { var parentStatement = elementAccess.Parent.AncestorsAndSelf().OfType <StatementSyntax>().FirstOrDefault(); var newParent = SyntaxFactory.IfStatement( SyntaxFactory.BinaryExpression( SyntaxKind.GreaterThanExpression, SyntaxFactory.MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, elementAccess.Expression, SyntaxFactory.IdentifierName("Count")), elementAccess.ArgumentList.Arguments.First().Expression ), parentStatement ); return(Task.FromResult(document.WithSyntaxRoot(root.ReplaceNode((SyntaxNode)parentStatement, newParent.WithAdditionalAnnotations(Formatter.Annotation))))); } ) ); }
public override async Task ComputeRefactoringsAsync(CodeRefactoringContext context) { var document = context.Document; if (document.Project.Solution.Workspace.Kind == WorkspaceKind.MiscellaneousFiles) { return; } var span = context.Span; if (!span.IsEmpty) { return; } var cancellationToken = context.CancellationToken; if (cancellationToken.IsCancellationRequested) { return; } var model = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); if (model.IsFromGeneratedCode(cancellationToken)) { return; } var root = await model.SyntaxTree.GetRootAsync(cancellationToken).ConfigureAwait(false); SyntaxToken token = root.FindToken(span.Start); if (!token.IsKind(SyntaxKind.IdentifierToken)) { return; } var property = token.Parent as PropertyDeclarationSyntax; if (property == null || !property.Identifier.Span.Contains(span)) { return; } if (IsEmptyComputedProperty(property)) { context.RegisterRefactoring( CodeActionFactory.Create( token.Span, DiagnosticSeverity.Info, GettextCatalog.GetString("Convert to auto-property"), t2 => { var newRoot = root.ReplaceNode(property, CreateNewProperty(property).WithAdditionalAnnotations(Formatter.Annotation).WithLeadingTrivia(property.GetLeadingTrivia())); return(Task.FromResult(document.WithSyntaxRoot(newRoot))); } ) ); return; } var field = GetBackingField(model, property); if (!IsValidField(field, property.Parent as TypeDeclarationSyntax)) { return; } //variable declarator->declaration->field declaration var backingFieldNode = root.FindNode(field.Locations.First().SourceSpan).Ancestors().OfType <FieldDeclarationSyntax>().First(); var propertyAnnotation = new SyntaxAnnotation(); var fieldAnnotation = new SyntaxAnnotation(); //annotate our property node and our field node root = root.ReplaceNode((SyntaxNode)property, property.WithAdditionalAnnotations(propertyAnnotation)); root = root.ReplaceNode((SyntaxNode)root.FindNode(backingFieldNode.Span), backingFieldNode.WithAdditionalAnnotations(fieldAnnotation)); context.RegisterRefactoring( CodeActionFactory.Create(token.Span, DiagnosticSeverity.Info, GettextCatalog.GetString("Convert to auto-property"), PerformAction(document, model, root, field.Name, CreateNewProperty(property), propertyAnnotation, fieldAnnotation)) ); }
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)); }
public sealed override async Task ComputeRefactoringsAsync(CodeRefactoringContext context) { var actions = await service.GetEncapsulateFieldCodeActionsAsync(context.Document, context.Span, context.CancellationToken).ConfigureAwait(false); foreach (var action in actions) context.RegisterRefactoring(action); }
public override async Task ComputeRefactoringsAsync(CodeRefactoringContext context) { var document = context.Document; var cancellationToken = context.CancellationToken; var(tupleExprOrTypeNode, tupleType) = await TryGetTupleInfoAsync( document, context.Span, cancellationToken).ConfigureAwait(false); if (tupleExprOrTypeNode == null || tupleType == null) { return; } // Check if the tuple type actually references another anonymous type inside of it. // If it does, we can't convert this. There is no way to describe this anonymous type // in the concrete type we create. var fields = tupleType.TupleElements; var containsAnonymousType = fields.Any(p => p.Type.ContainsAnonymousType()); if (containsAnonymousType) { return; } var capturedTypeParameters = fields.Select(p => p.Type) .SelectMany(t => t.GetReferencedTypeParameters()) .Distinct() .ToImmutableArray(); var scopes = ArrayBuilder <CodeAction> .GetInstance(); scopes.Add(CreateAction(context, Scope.ContainingMember)); // If we captured any Method type-parameters, we can only replace the tuple types we // find in the containing method. No other tuple types in other members would be able // to reference this type parameter. if (!capturedTypeParameters.Any(tp => tp.TypeParameterKind == TypeParameterKind.Method)) { var containingType = tupleExprOrTypeNode.GetAncestor <TTypeBlockSyntax>(); if (containingType != null) { scopes.Add(CreateAction(context, Scope.ContainingType)); } // If we captured any Type type-parameters, we can only replace the tuple // types we find in the containing type. No other tuple types in other // types would be able to reference this type parameter. if (!capturedTypeParameters.Any(tp => tp.TypeParameterKind == TypeParameterKind.Type)) { // To do a global find/replace of matching tuples, we need to search for documents // containing tuples *and* which have the names of the tuple fields in them. That means // the tuple field name must exist in the document. // // this means we can only find tuples like ```(x: 1, ...)``` but not ```(1, 2)```. The // latter has members called Item1 and Item2, but those names don't show up in source. if (fields.All(f => f.CorrespondingTupleField != f)) { scopes.Add(CreateAction(context, Scope.ContainingProject)); scopes.Add(CreateAction(context, Scope.DependentProjects)); } } } context.RegisterRefactoring(new CodeAction.CodeActionWithNestedActions( FeaturesResources.Convert_to_struct, scopes.ToImmutableAndFree(), isInlinable: false)); }
public override async Task ComputeRefactoringsAsync(CodeRefactoringContext context) { SyntaxNode root = await context.Document.GetSyntaxRootAsync(context.CancellationToken); MemberDeclarationSyntax memberDeclaration = root .FindNode(context.Span, getInnermostNodeForTie: true)? .FirstAncestorOrSelf <MemberDeclarationSyntax>(); if (memberDeclaration == null) { return; } SplitAttributesRefactoring.Refactor(context, memberDeclaration); MergeAttributesRefactoring.Refactor(context, memberDeclaration); switch (memberDeclaration.Kind()) { case SyntaxKind.MethodDeclaration: case SyntaxKind.IndexerDeclaration: case SyntaxKind.PropertyDeclaration: case SyntaxKind.OperatorDeclaration: case SyntaxKind.ConversionOperatorDeclaration: case SyntaxKind.ConstructorDeclaration: case SyntaxKind.EventDeclaration: case SyntaxKind.NamespaceDeclaration: case SyntaxKind.ClassDeclaration: case SyntaxKind.StructDeclaration: case SyntaxKind.InterfaceDeclaration: { if (MemberDeclarationRefactoring.CanBeRemoved(context, memberDeclaration)) { context.RegisterRefactoring( "Remove " + SyntaxHelper.GetSyntaxNodeName(memberDeclaration), cancellationToken => MemberDeclarationRefactoring.RemoveMemberAsync(context.Document, memberDeclaration, cancellationToken)); } if (MemberDeclarationRefactoring.CanBeDuplicated(context, memberDeclaration)) { context.RegisterRefactoring( "Duplicate " + SyntaxHelper.GetSyntaxNodeName(memberDeclaration), cancellationToken => MemberDeclarationRefactoring.DuplicateMemberAsync(context.Document, memberDeclaration, cancellationToken)); } break; } } if (root.FindTrivia(context.Span.Start).IsWhitespaceOrEndOfLine()) { SwapMembersRefactoring.Refactor(context, memberDeclaration); } switch (memberDeclaration.Kind()) { case SyntaxKind.MethodDeclaration: { await ComputeRefactoringsAsync(context, (MethodDeclarationSyntax)memberDeclaration); break; } case SyntaxKind.IndexerDeclaration: { ComputeRefactorings(context, (IndexerDeclarationSyntax)memberDeclaration); break; } case SyntaxKind.PropertyDeclaration: { await ComputeRefactoringsAsync(context, (PropertyDeclarationSyntax)memberDeclaration); break; } case SyntaxKind.EventFieldDeclaration: { await ComputeRefactoringsAsync(context, (EventFieldDeclarationSyntax)memberDeclaration); break; } } }
public sealed override async Task ComputeRefactoringsAsync(CodeRefactoringContext context) { var(document, _, cancellationToken) = context; var numericToken = await GetNumericTokenAsync(context).ConfigureAwait(false); if (numericToken == default || numericToken.ContainsDiagnostics) { return; } var syntaxNode = numericToken.GetRequiredParent(); var semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); var symbol = semanticModel.GetTypeInfo(syntaxNode, cancellationToken).Type; if (symbol == null) { return; } if (!IsIntegral(symbol.SpecialType)) { return; } var valueOpt = semanticModel.GetConstantValue(syntaxNode); if (!valueOpt.HasValue) { return; } var root = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); var value = IntegerUtilities.ToInt64(valueOpt.Value); var numericText = numericToken.ToString(); var(hexPrefix, binaryPrefix) = GetNumericLiteralPrefixes(); var(prefix, number, suffix) = GetNumericLiteralParts(numericText, hexPrefix, binaryPrefix); var kind = string.IsNullOrEmpty(prefix) ? NumericKind.Decimal : prefix.Equals(hexPrefix, StringComparison.OrdinalIgnoreCase) ? NumericKind.Hexadecimal : prefix.Equals(binaryPrefix, StringComparison.OrdinalIgnoreCase) ? NumericKind.Binary : NumericKind.Unknown; if (kind == NumericKind.Unknown) { return; } if (kind != NumericKind.Decimal) { RegisterRefactoringWithResult(value.ToString(), FeaturesResources.Convert_to_decimal); } if (kind != NumericKind.Binary) { RegisterRefactoringWithResult(binaryPrefix + Convert.ToString(value, toBase: 2), FeaturesResources.Convert_to_binary); } if (kind != NumericKind.Hexadecimal) { RegisterRefactoringWithResult(hexPrefix + value.ToString("X"), FeaturesResources.Convert_to_hex); } const string DigitSeparator = "_"; if (numericText.Contains(DigitSeparator)) { RegisterRefactoringWithResult(prefix + number.Replace(DigitSeparator, string.Empty), FeaturesResources.Remove_separators); } else { switch (kind) { case NumericKind.Decimal when number.Length > 3: RegisterRefactoringWithResult(AddSeparators(number, interval: 3), FeaturesResources.Separate_thousands); break; case NumericKind.Hexadecimal when number.Length > 4: RegisterRefactoringWithResult(hexPrefix + AddSeparators(number, interval: 4), FeaturesResources.Separate_words); break; case NumericKind.Binary when number.Length > 4: RegisterRefactoringWithResult(binaryPrefix + AddSeparators(number, interval: 4), FeaturesResources.Separate_nibbles); break; } } void RegisterRefactoringWithResult(string text, string title) { context.RegisterRefactoring( new MyCodeAction(title, c => ReplaceTokenAsync(document, root, numericToken, value, text, suffix)), numericToken.Span); } }
public override async Task ComputeRefactoringsAsync(CodeRefactoringContext context) { var document = context.Document; var textSpan = context.Span; var cancellationToken = context.CancellationToken; if (document.Project.Solution.Workspace.Kind == WorkspaceKind.MiscellaneousFiles || document.IsGeneratedCode(cancellationToken)) { return; } var state = await State.CreateAsync(this, document, textSpan, cancellationToken).ConfigureAwait(false); if (state == null) { return; } // No move file action if rootnamespace isn't a prefix of current declared namespace if (state.RelativeDeclaredNamespace != null) { // These code actions try to move file to a new location based on declared namespace // and the default namespace of the project. The new location is a list of folders // determined by the relateive part of the declared namespace compare to the default namespace. // // For example, if he default namespace is `A.B.C`, file path is // "[project root dir]\Class1.cs" and declared namespace in the file is // `A.B.C.D.E`, then this action will move the file to [project root dir]\D\E\Class1.cs". . // // We also try to use existing folders as target if possible, using the same example above, // if folder "[project root dir]\D.E\" already exist, we will also offer to move file to // "[project root dir]\D.E\Class1.cs". context.RegisterRefactorings(MoveFileCodeAction.Create(state)); } // No change namespace action if we can't construct a valid namespace from rootnamespace and folder names. if (state.TargetNamespace != null) { // This code action tries to change the name of the namespace declaration to // match the folder hierarchy of the document. The new namespace is constructed // by concatenating the default namespace of the project and all the folders in // the file path up to the project root. // // For example, if he default namespace is `A.B.C`, file path is // "[project root dir]\D\E\F\Class1.cs" and declared namespace in the file is // `Foo.Bar.Baz`, then this action will change the namespace declaration // to `A.B.C.D.E.F`. // // Note that it also handles the case where the target namespace or declared namespace // is global namespace, i.e. default namespace is "" and the file is located at project // root directory, and no namespace declaration in the document, respectively. var service = document.GetLanguageService <IChangeNamespaceService>(); var solutionChangeAction = new ChangeNamespaceCodeAction( state.TargetNamespace.Length == 0 ? FeaturesResources.Change_to_global_namespace : string.Format(FeaturesResources.Change_namespace_to_0, state.TargetNamespace), token => service.ChangeNamespaceAsync(document, state.Container, state.TargetNamespace, token)); context.RegisterRefactoring(solutionChangeAction); } }
public override async Task ComputeRefactoringsAsync(CodeRefactoringContext context) { SyntaxNode root = await context.Document.GetSyntaxRootAsync(context.CancellationToken); InvocationExpressionSyntax invocationExpression = root .FindNode(context.Span, getInnermostNodeForTie: true)? .FirstAncestorOrSelf <InvocationExpressionSyntax>(); if (invocationExpression != null && invocationExpression.Expression != null && invocationExpression.ArgumentList != null && invocationExpression.Expression.IsKind(SyntaxKind.SimpleMemberAccessExpression) && ((MemberAccessExpressionSyntax)invocationExpression.Expression).Name?.Span.Contains(context.Span) == true && context.Document.SupportsSemanticModel) { SemanticModel semanticModel = await context.Document.GetSemanticModelAsync(context.CancellationToken); ConvertEnumerableMethodToElementAccessRefactoring.Refactor(context, invocationExpression, semanticModel); var methodSymbol = semanticModel.GetSymbolInfo(invocationExpression, context.CancellationToken).Symbol as IMethodSymbol; if (methodSymbol == null) { return; } INamedTypeSymbol enumerable = semanticModel.Compilation.GetTypeByMetadataName("System.Linq.Enumerable"); if (enumerable == null) { return; } int argumentIndex = (methodSymbol.ReducedFrom != null) ? 0 : 1; methodSymbol = methodSymbol.ReducedFrom ?? methodSymbol.ConstructedFrom; if (methodSymbol.Equals(GetMethod(enumerable, "Any"))) { ExpressionSyntax expression = GetExpression(invocationExpression, argumentIndex); if (expression != null) { context.RegisterRefactoring( "Change 'Any' to 'All'", cancellationToken => { return(CreateChangedDocumentAsync( context.Document, invocationExpression, "All", expression, cancellationToken)); }); } } else if (methodSymbol.Equals(GetMethod(enumerable, "All"))) { ExpressionSyntax expression = GetExpression(invocationExpression, argumentIndex); if (expression != null) { context.RegisterRefactoring( "Change 'All' to 'Any'", cancellationToken => { return(CreateChangedDocumentAsync( context.Document, invocationExpression, "Any", expression, cancellationToken)); }); } } } }
public override async Task ComputeRefactoringsAsync(CodeRefactoringContext context) { var document = context.Document; var service = document.GetLanguageService <IReplaceMethodWithPropertyService>(); if (service == null) { return; } var cancellationToken = context.CancellationToken; var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); var position = context.Span.Start; var token = root.FindToken(position); if (!token.Span.Contains(context.Span)) { return; } var methodDeclaration = service.GetMethodDeclaration(token); if (methodDeclaration == null) { return; } // Ok, we're in the signature of the method. Now see if the method is viable to be // replaced with a property. var methodName = service.GetMethodName(methodDeclaration); var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); var methodSymbol = semanticModel.GetDeclaredSymbol(methodDeclaration) as IMethodSymbol; if (!IsValidGetMethod(methodSymbol)) { return; } var hasGetPrefix = HasGetPrefix(methodName); var propertyName = hasGetPrefix ? methodName.Substring(GetPrefix.Length) : methodName; var nameChanged = hasGetPrefix; // Looks good! context.RegisterRefactoring(new ReplaceMethodWithPropertyCodeAction( string.Format(FeaturesResources.Replace0WithProperty, methodName), c => ReplaceMethodsWithProperty(context.Document, propertyName, nameChanged, methodSymbol, setMethod: null, cancellationToken: c), methodName)); // If this method starts with 'Get' see if there's an associated 'Set' method we could // replace as well. if (hasGetPrefix) { var setMethod = FindSetMethod(methodSymbol); if (setMethod != null) { context.RegisterRefactoring(new ReplaceMethodWithPropertyCodeAction( string.Format(FeaturesResources.Replace0and1WithProperty, methodName, setMethod.Name), c => ReplaceMethodsWithProperty(context.Document, propertyName, nameChanged, methodSymbol, setMethod, cancellationToken: c), methodName + "-get/set")); } } }
public override async Task ComputeRefactoringsAsync(CodeRefactoringContext context) { var(document, textSpan, cancellationToken) = context; var forStatement = await context.TryGetRelevantNodeAsync <TForStatementSyntax>().ConfigureAwait(false); if (forStatement == null) { return; } if (!TryGetForStatementComponents(forStatement, out var iterationVariable, out var initializer, out var memberAccess, out var stepValueExpressionOpt, cancellationToken)) { return; } var syntaxFacts = document.GetRequiredLanguageService <ISyntaxFactsService>(); syntaxFacts.GetPartsOfMemberAccessExpression(memberAccess, out var collectionExpressionNode, out var memberAccessNameNode); var collectionExpression = (TExpressionSyntax)collectionExpressionNode; syntaxFacts.GetNameAndArityOfSimpleName(memberAccessNameNode, out var memberAccessName, out _); if (memberAccessName is not nameof(Array.Length) and not nameof(IList.Count)) { return; } var semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); // Make sure it's a single-variable for loop and that we're not a loop where we're // referencing some previously declared symbol. i.e // VB allows: // // dim i as integer // for i = 0 to ... // // We can't convert this as it would change important semantics. // NOTE: we could potentially update this if we saw that the variable was not used // after the for-loop. But, for now, we'll just be conservative and assume this means // the user wanted the 'i' for some other purpose and we should keep things as is. if (semanticModel.GetOperation(forStatement, cancellationToken) is not ILoopOperation operation || operation.Locals.Length != 1) { return; } // Make sure we're starting at 0. var initializerValue = semanticModel.GetConstantValue(initializer, cancellationToken); if (!(initializerValue.HasValue && initializerValue.Value is 0)) { return; } // Make sure we're incrementing by 1. if (stepValueExpressionOpt != null) { var stepValue = semanticModel.GetConstantValue(stepValueExpressionOpt); if (!(stepValue.HasValue && stepValue.Value is 1)) { return; } } var collectionType = semanticModel.GetTypeInfo(collectionExpression, cancellationToken); if (collectionType.Type == null || collectionType.Type.TypeKind == TypeKind.Error) { return; } var containingType = semanticModel.GetEnclosingNamedType(textSpan.Start, cancellationToken); if (containingType == null) { return; } var ienumerableType = semanticModel.Compilation.GetSpecialType(SpecialType.System_Collections_Generic_IEnumerable_T); var ienumeratorType = semanticModel.Compilation.GetSpecialType(SpecialType.System_Collections_Generic_IEnumerator_T); // make sure the collection can be iterated. if (!TryGetIterationElementType( containingType, collectionType.Type, ienumerableType, ienumeratorType, out var iterationType)) { return; } // If the user uses the iteration variable for any other reason, we can't convert this. var bodyStatements = GetBodyStatements(forStatement); foreach (var statement in bodyStatements) { if (IterationVariableIsUsedForMoreThanCollectionIndex(statement)) { return; } } // Looks good. We can convert this. var title = GetTitle(); context.RegisterRefactoring( CodeAction.Create( title, c => ConvertForToForEachAsync( document, forStatement, iterationVariable, collectionExpression, containingType, collectionType.Type, iterationType, c), title), forStatement.Span); return; // local functions bool IterationVariableIsUsedForMoreThanCollectionIndex(SyntaxNode current) { if (syntaxFacts.IsIdentifierName(current)) { syntaxFacts.GetNameAndArityOfSimpleName(current, out var name, out _); if (name == iterationVariable.ValueText) { // found a reference. make sure it's only used inside something like // list[i] if (!syntaxFacts.IsSimpleArgument(current.Parent) || !syntaxFacts.IsElementAccessExpression(current.Parent?.Parent?.Parent)) { // used in something other than accessing into a collection. // can't convert this for-loop. return(true); } var arguments = syntaxFacts.GetArgumentsOfArgumentList(current.Parent.Parent); if (arguments.Count != 1) { // was used in a multi-dimensional indexing. Can't conver this. return(true); } var expr = syntaxFacts.GetExpressionOfElementAccessExpression(current.Parent.Parent.Parent); if (!syntaxFacts.AreEquivalent(expr, collectionExpression)) { // was indexing into something other than the collection. // can't convert this for-loop. return(true); } // this usage of the for-variable is fine. } } foreach (var child in current.ChildNodesAndTokens()) { if (child.IsNode) { if (IterationVariableIsUsedForMoreThanCollectionIndex(child.AsNode() !)) { return(true); } } } return(false); } }
public override async Task ComputeRefactoringsAsync(CodeRefactoringContext context) { var document = context.Document; if (document.Project.Solution.Workspace.Kind == WorkspaceKind.MiscellaneousFiles) { return; } var span = context.Span; if (!span.IsEmpty) { return; } var cancellationToken = context.CancellationToken; if (cancellationToken.IsCancellationRequested) { return; } var model = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); if (model.IsFromGeneratedCode(cancellationToken)) { return; } var root = await model.SyntaxTree.GetRootAsync(cancellationToken).ConfigureAwait(false); var token = root.FindToken(span.Start); var parameter = token.Parent as ParameterSyntax; if (parameter != null) { var ctor = parameter.Parent.Parent as ConstructorDeclarationSyntax; if (ctor == null) { return; } context.RegisterRefactoring( CodeActionFactory.Create( parameter.Span, DiagnosticSeverity.Info, GettextCatalog.GetString("Initialize field from parameter"), t2 => { var newFieldName = NameProposalService.GetNameProposal(parameter.Identifier.ValueText, SyntaxKind.FieldDeclaration, Accessibility.Private, false, context.Document, ctor.SpanStart); var newField = SyntaxFactory.FieldDeclaration( SyntaxFactory.VariableDeclaration( parameter.Type, SyntaxFactory.SingletonSeparatedList <VariableDeclaratorSyntax>(SyntaxFactory.VariableDeclarator(newFieldName))) ).WithModifiers(SyntaxFactory.TokenList(SyntaxFactory.Token(SyntaxKind.ReadOnlyKeyword))) .WithAdditionalAnnotations(Formatter.Annotation); var assignmentStatement = SyntaxFactory.ExpressionStatement( SyntaxFactory.AssignmentExpression( SyntaxKind.SimpleAssignmentExpression, SyntaxFactory.MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, SyntaxFactory.ThisExpression(), SyntaxFactory.IdentifierName(newFieldName)), SyntaxFactory.IdentifierName(newFieldName) ) ).WithAdditionalAnnotations(Formatter.Annotation); var trackedRoot = root.TrackNodes(ctor); var newRoot = trackedRoot.InsertNodesBefore(trackedRoot.GetCurrentNode(ctor), new List <SyntaxNode>() { newField }); newRoot = newRoot.ReplaceNode(newRoot.GetCurrentNode(ctor), ctor.WithBody( ctor.Body.WithStatements(SyntaxFactory.List <StatementSyntax>(new[] { assignmentStatement }.Concat(ctor.Body.Statements))) )); return(Task.FromResult(document.WithSyntaxRoot(newRoot))); }) ); } }
public override async Task ComputeRefactoringsAsync(CodeRefactoringContext context) { var document = context.Document; if (document.Project.Solution.Workspace.Kind == WorkspaceKind.MiscellaneousFiles) { return; } var span = context.Span; if (!span.IsEmpty) { return; } var cancellationToken = context.CancellationToken; if (cancellationToken.IsCancellationRequested) { return; } var model = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); if (model.IsFromGeneratedCode(cancellationToken)) { return; } var root = await model.SyntaxTree.GetRootAsync(cancellationToken).ConfigureAwait(false); var token = root.FindToken(span.Start); if (!token.IsKind(SyntaxKind.IdentifierToken)) { return; } var node = token.Parent as MemberDeclarationSyntax; if (node == null) { return; } var declaredSymbol = model.GetDeclaredSymbol(node, cancellationToken); if (declaredSymbol == null || !string.IsNullOrEmpty(declaredSymbol.GetDocumentationCommentXml(null, false, cancellationToken))) { return; } string documentation; var baseMember = GetBaseMember(declaredSymbol, out documentation, cancellationToken); if (baseMember == null || string.IsNullOrEmpty(documentation)) { return; } XDocument doc = XDocument.Parse(documentation); var rootElement = doc.Elements().First(); var inner = string.Join(System.Environment.NewLine, rootElement.Nodes().Select(n => n.ToString())).Trim(); if (string.IsNullOrEmpty(inner)) { return; } // "Copy comments from interface" context.RegisterRefactoring( CodeActionFactory.Create( span, DiagnosticSeverity.Info, baseMember.ContainingType != null && baseMember.ContainingType.TypeKind == TypeKind.Interface ? GettextCatalog.GetString("Copy comments from interface") : GettextCatalog.GetString("Copy comments from base"), t2 => { var triva = node.GetLeadingTrivia(); var indentTrivia = triva.FirstOrDefault(t => t.IsKind(SyntaxKind.WhitespaceTrivia)); var indent = indentTrivia.ToString(); string[] lines = NewLine.SplitLines(inner); for (int i = 0; i < lines.Length; i++) { lines[i] = indent + "/// " + lines[i].Trim(); } var eol = "\r\n"; int idx = 0; while (idx < triva.Count && triva[idx].IsKind(SyntaxKind.EndOfLineTrivia)) { idx++; } triva = triva.Insert(idx, SyntaxFactory.SyntaxTrivia(SyntaxKind.SingleLineCommentTrivia, string.Join(eol, lines) + eol)); var newRoot = root.ReplaceNode(node, node.WithLeadingTrivia(triva)); return(Task.FromResult(document.WithSyntaxRoot(newRoot))); } ) ); }
public override async Task ComputeRefactoringsAsync(CodeRefactoringContext context) { var document = context.Document; if (document.Project.Solution.Workspace.Kind == WorkspaceKind.MiscellaneousFiles) { return; } var span = context.Span; if (!span.IsEmpty) { return; } var cancellationToken = context.CancellationToken; if (cancellationToken.IsCancellationRequested) { return; } var model = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); if (model.IsFromGeneratedCode(cancellationToken)) { return; } var root = await model.SyntaxTree.GetRootAsync(cancellationToken).ConfigureAwait(false); var token = root.FindToken(span.Start); var parameter = token.Parent as ParameterSyntax; if (parameter != null) { var ctor = parameter.Parent.Parent as ConstructorDeclarationSyntax; if (ctor == null) { return; } context.RegisterRefactoring( CodeActionFactory.Create( parameter.Span, DiagnosticSeverity.Info, GettextCatalog.GetString("Initialize auto-property from parameter"), t2 => { var propertyName = GetPropertyName(parameter.Identifier.ToString()); var accessorDeclList = new SyntaxList <AccessorDeclarationSyntax>().Add(SyntaxFactory.AccessorDeclaration(SyntaxKind.GetAccessorDeclaration).WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.SemicolonToken))).Add(SyntaxFactory.AccessorDeclaration(SyntaxKind.SetAccessorDeclaration).WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.SemicolonToken))); var newProperty = SyntaxFactory.PropertyDeclaration(parameter.Type, propertyName) .WithAccessorList(SyntaxFactory.AccessorList(accessorDeclList)) .WithModifiers(SyntaxFactory.TokenList(SyntaxFactory.Token(SyntaxKind.PublicKeyword))) .WithAdditionalAnnotations(Formatter.Annotation); var assignmentStatement = SyntaxFactory.ExpressionStatement( SyntaxFactory.AssignmentExpression( SyntaxKind.SimpleAssignmentExpression, propertyName != parameter.Identifier.ToString() ? (ExpressionSyntax)SyntaxFactory.IdentifierName(propertyName) : SyntaxFactory.MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, SyntaxFactory.ThisExpression(), SyntaxFactory.IdentifierName(parameter.Identifier)), SyntaxFactory.IdentifierName(parameter.Identifier) ) ).WithAdditionalAnnotations(Formatter.Annotation); root = root.TrackNodes(ctor); var newRoot = root.InsertNodesBefore(root.GetCurrentNode(ctor), new List <SyntaxNode>() { newProperty }); newRoot = newRoot.ReplaceNode(newRoot.GetCurrentNode(ctor), ctor.WithBody( ctor.Body.WithStatements(SyntaxFactory.List <StatementSyntax>(new[] { assignmentStatement }.Concat(ctor.Body.Statements))) )); return(Task.FromResult(document.WithSyntaxRoot(newRoot))); }) ); } }
public sealed override async Task ComputeRefactoringsAsync(CodeRefactoringContext context) { if (!options.IsFeatureEnabled(FeatureIdentifiers.AddInitializedFieldRefactoring)) { return; } if (context.Document.Project.Solution.Workspace.Kind == WorkspaceKind.MiscellaneousFiles) { return; } if (!context.Span.IsEmpty) { return; } var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); var node = root.FindNode(context.Span); var parameter = node as ParameterSyntax; if (parameter == null && context.Span.Start > 0) { var span = new TextSpan(context.Span.Start - 1, 0); node = root.FindNode(span); parameter = node as ParameterSyntax; if (parameter == null) { return; } } if (parameter == null) { return; } var constructor = parameter.Ancestors().OfType <ConstructorDeclarationSyntax>().FirstOrDefault(); if (constructor == null) { return; } var model = await context.Document.GetSemanticModelAsync(context.CancellationToken); var constructorSymbol = model.GetDeclaredSymbol(constructor, context.CancellationToken); if (constructorSymbol != null && constructorSymbol.IsStatic) { return; } if (CheckIfAlreadyInitialized(model, parameter, constructor, context.CancellationToken)) { return; } var existingField = FindExistingField(model, parameter, parameter.GetParentTypeDeclaration(), context.CancellationToken); var action = CodeAction.Create(existingField != null ? Resources.InitializeExistingField : Resources.AddInitializedField, cancellationToken => InitializeFieldAsync(context.Document, root, parameter, constructor, cancellationToken)); context.RegisterRefactoring(action); }
public override async Task ComputeRefactoringsAsync(CodeRefactoringContext context) { var(document, span, cancellationToken) = context; if (!span.IsEmpty) { return; } var tree = await document.GetRequiredSyntaxTreeAsync(cancellationToken).ConfigureAwait(false); var root = await tree.GetRootAsync(cancellationToken).ConfigureAwait(false); if (document.Project.AnalyzerOptions.TryGetEditorConfigOption(CodeStyleOptions2.FileHeaderTemplate, tree, out string fileHeaderTemplate) && !string.IsNullOrEmpty(fileHeaderTemplate)) { // If we have a defined file header template, allow the analyzer and code fix to handle it return; } var position = span.Start; var firstToken = root.GetFirstToken(); if (!firstToken.FullSpan.IntersectsWith(position)) { return; } var syntaxFacts = document.GetRequiredLanguageService <ISyntaxFactsService>(); var banner = syntaxFacts.GetFileBanner(root); if (banner.Length > 0) { // Already has a banner. return; } // Process the other documents in this document's project. Look at the // ones that we can get a root from (without having to parse). Then // look at the ones we'd need to parse. var siblingDocumentsAndRoots = document.Project.Documents .Where(d => d != document) .Select(d => { d.TryGetSyntaxRoot(out var siblingRoot); return(document: d, root: siblingRoot); }) .OrderBy((t1, t2) => (t1.root != null) == (t2.root != null) ? 0 : t1.root != null ? -1 : 1); foreach (var(siblingDocument, siblingRoot) in siblingDocumentsAndRoots) { cancellationToken.ThrowIfCancellationRequested(); var siblingBanner = await TryGetBannerAsync(siblingDocument, siblingRoot, cancellationToken).ConfigureAwait(false); if (siblingBanner.Length > 0 && !siblingDocument.IsGeneratedCode(cancellationToken)) { context.RegisterRefactoring( new MyCodeAction(_ => AddBannerAsync(document, root, siblingDocument, siblingBanner)), new Text.TextSpan(position, length: 0)); return; } } }
public async Task ComputeRefactoringsAsync( CodeRefactoringContext context, SyntaxNode root, CancellationToken cancellationToken) { if (context.Span.Length > 0) { return; } var position = context.Span.Start; var token = root.FindToken(position); if (token.Span.Start == position && IsCloseParenOrComma(token)) { token = token.GetPreviousToken(); if (token.Span.End != position) { return; } } var argument = root.FindNode(token.Span).FirstAncestorOrSelfUntil <TBaseArgumentSyntax>(node => node is TArgumentListSyntax) as TSimpleArgumentSyntax; if (argument == null) { return; } if (!IsPositionalArgument(argument)) { return; } // Arguments can be arbitrarily large. Only offer this feature if the caret is on hte // line that the argument starts on. var document = context.Document; var sourceText = await document.GetTextAsync(cancellationToken).ConfigureAwait(false); var argumentStartLine = sourceText.Lines.GetLineFromPosition(argument.Span.Start).LineNumber; var caretLine = sourceText.Lines.GetLineFromPosition(position).LineNumber; if (argumentStartLine != caretLine) { return; } var receiver = GetReceiver(argument); if (receiver == null) { return; } if (receiver.ContainsDiagnostics) { return; } var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); var symbol = semanticModel.GetSymbolInfo(receiver, cancellationToken).Symbol; if (symbol == null) { return; } var parameters = symbol.GetParameters(); if (parameters.IsDefaultOrEmpty) { return; } var argumentList = argument.Parent as TArgumentListSyntax; if (argumentList == null) { return; } var arguments = GetArguments(argumentList); var argumentCount = arguments.Count; var argumentIndex = arguments.IndexOf(argument); if (argumentIndex >= parameters.Length) { return; } if (!IsLegalToAddNamedArguments(parameters, argumentCount)) { return; } for (var i = argumentIndex; i < argumentCount; i++) { if (!(arguments[i] is TSimpleArgumentSyntax)) { return; } } var argumentName = parameters[argumentIndex].Name; if (this.SupportsNonTrailingNamedArguments(root.SyntaxTree.Options) && argumentIndex < argumentCount - 1) { context.RegisterRefactoring( new MyCodeAction( string.Format(FeaturesResources.Add_argument_name_0, argumentName), c => AddNamedArgumentsAsync(root, document, argument, parameters, argumentIndex, includingTrailingArguments: false))); context.RegisterRefactoring( new MyCodeAction( string.Format(FeaturesResources.Add_argument_name_0_including_trailing_arguments, argumentName), c => AddNamedArgumentsAsync(root, document, argument, parameters, argumentIndex, includingTrailingArguments: true))); } else { context.RegisterRefactoring( new MyCodeAction( string.Format(FeaturesResources.Add_argument_name_0, argumentName), c => AddNamedArgumentsAsync(root, document, argument, parameters, argumentIndex, includingTrailingArguments: true))); } }
public sealed override async Task ComputeRefactoringsAsync(CodeRefactoringContext context) { var document = context.Document; var cancellationToken = context.CancellationToken; var syntaxFacts = document.GetLanguageService <ISyntaxFactsService>(); var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); var numericToken = await root.SyntaxTree.GetTouchingTokenAsync(context.Span.Start, token => syntaxFacts.IsNumericLiteralExpression(token.Parent), cancellationToken).ConfigureAwait(false); if (numericToken == default) { return; } if (numericToken.ContainsDiagnostics) { return; } if (context.Span.Length > 0 && context.Span != numericToken.Span) { return; } var syntaxNode = numericToken.Parent; var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); var symbol = semanticModel.GetTypeInfo(syntaxNode, cancellationToken).Type; if (symbol == null) { return; } if (!IsIntegral(symbol.SpecialType)) { return; } var valueOpt = semanticModel.GetConstantValue(syntaxNode); if (!valueOpt.HasValue) { return; } var value = IntegerUtilities.ToInt64(valueOpt.Value); var numericText = numericToken.ToString(); var(hexPrefix, binaryPrefix) = GetNumericLiteralPrefixes(); var(prefix, number, suffix) = GetNumericLiteralParts(numericText, hexPrefix, binaryPrefix); var kind = string.IsNullOrEmpty(prefix) ? NumericKind.Decimal : prefix.Equals(hexPrefix, StringComparison.OrdinalIgnoreCase) ? NumericKind.Hexadecimal : prefix.Equals(binaryPrefix, StringComparison.OrdinalIgnoreCase) ? NumericKind.Binary : NumericKind.Unknown; if (kind == NumericKind.Unknown) { return; } if (kind != NumericKind.Decimal) { RegisterRefactoringWithResult(value.ToString(), FeaturesResources.Convert_to_decimal); } if (kind != NumericKind.Binary) { RegisterRefactoringWithResult(binaryPrefix + Convert.ToString(value, 2), FeaturesResources.Convert_to_binary); } if (kind != NumericKind.Hexadecimal) { RegisterRefactoringWithResult(hexPrefix + value.ToString("X"), FeaturesResources.Convert_to_hex); } const string DigitSeparator = "_"; if (numericText.Contains(DigitSeparator)) { RegisterRefactoringWithResult(prefix + number.Replace(DigitSeparator, string.Empty), FeaturesResources.Remove_separators); } else { switch (kind) { case NumericKind.Decimal when number.Length > 3: RegisterRefactoringWithResult(AddSeparators(number, 3), FeaturesResources.Separate_thousands); break; case NumericKind.Hexadecimal when number.Length > 4: RegisterRefactoringWithResult(hexPrefix + AddSeparators(number, 4), FeaturesResources.Separate_words); break; case NumericKind.Binary when number.Length > 4: RegisterRefactoringWithResult(binaryPrefix + AddSeparators(number, 4), FeaturesResources.Separate_nibbles); break; } } void RegisterRefactoringWithResult(string text, string title) { context.RegisterRefactoring(new MyCodeAction(title, c => { var generator = SyntaxGenerator.GetGenerator(document); var updatedToken = generator.NumericLiteralToken(text + suffix, (ulong)value) .WithTriviaFrom(numericToken); var updatedRoot = root.ReplaceToken(numericToken, updatedToken); return(Task.FromResult(document.WithSyntaxRoot(updatedRoot))); })); } }