public override async Task ComputeRefactoringsAsync(CodeRefactoringContext context)
        {
            var document = context.Document;
            var textSpan = context.Span;
            var cancellationToken = context.CancellationToken;

            // TODO: https://github.com/dotnet/roslyn/issues/5778
            // Not supported in REPL for now.
            if (document.Project.IsSubmission)
            {
                return;
            }

            if (document.Project.Solution.Workspace.Kind == WorkspaceKind.MiscellaneousFiles)
            {
                return;
            }

            var service = document.GetLanguageService<IGenerateDefaultConstructorsService>();
            var actions = await service.GenerateDefaultConstructorsAsync(document, textSpan, cancellationToken).ConfigureAwait(false);
            if (!actions.IsDefault)
            {
                context.RegisterRefactorings(actions);
            }
        }
        public override async Task ComputeRefactoringsAsync(CodeRefactoringContext context)
        {
            var document = context.Document;
            var textSpan = context.Span;
            var cancellationToken = context.CancellationToken;

            var workspace = document.Project.Solution.Workspace;
            if (workspace.Kind == WorkspaceKind.MiscellaneousFiles)
            {
                return;
            }

            var generatedCodeRecognitionService = workspace.Services.GetService<IGeneratedCodeRecognitionService>();
            if (generatedCodeRecognitionService.IsGeneratedCode(document))
            {
                return;
            }

            var service = document.GetLanguageService<IMoveTypeService>();
            var actions = await service.GetRefactoringAsync(document, textSpan, cancellationToken).ConfigureAwait(false);
            if (!actions.IsDefault)
            {
                context.RegisterRefactorings(actions);
            }
        }
        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)
            {
                return;
            }

            var service = document.GetLanguageService<IGenerateEqualsAndGetHashCodeService>();
            var actions = await service.GenerateEqualsAndGetHashCodeAsync(document, textSpan, cancellationToken).ConfigureAwait(false);
            if (!actions.IsDefault)
            {
                context.RegisterRefactorings(actions);
            }
        }
        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);
        }
예제 #5
0
 public async Task <SyntaxNode?> GetPropertyDeclarationAsync(CodeRefactoringContext context)
 => await context.TryGetRelevantNodeAsync <TPropertySyntax>().ConfigureAwait(false);
        public override async Task ComputeRefactoringsAsync(CodeRefactoringContext context)
        {
            var position          = context.Span.Start;
            var document          = context.Document;
            var cancellationToken = context.CancellationToken;

            var syntaxTree = await document.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false);

            var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);

            var token         = root.FindToken(position);
            var parameterNode = GetParameterNode(token, position);

            if (parameterNode == null)
            {
                return;
            }

            // Only offered when there isn't a selection, or the selection exactly selects
            // a parameter name.
            var syntaxFacts = document.GetLanguageService <ISyntaxFactsService>();

            if (!context.Span.IsEmpty)
            {
                var parameterName = syntaxFacts.GetNameOfParameter(parameterNode);
                if (parameterName == null || parameterName.Value.Span != context.Span)
                {
                    return;
                }
            }

            var functionDeclaration = parameterNode.FirstAncestorOrSelf <SyntaxNode>(IsFunctionDeclaration);

            if (functionDeclaration == null)
            {
                return;
            }

            var parameterDefault = syntaxFacts.GetDefaultOfParameter(parameterNode);

            // Don't offer inside the "=initializer" of a parameter
            if (parameterDefault?.Span.Contains(position) == true)
            {
                return;
            }

            var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);

            // we can't just call GetDeclaredSymbol on functionDeclaration because it could an anonymous function,
            // so first we have to get the parameter symbol and then its containing method symbol
            var parameter = (IParameterSymbol)semanticModel.GetDeclaredSymbol(parameterNode, cancellationToken);

            if (parameter == null || parameter.Name == "")
            {
                return;
            }

            var method = (IMethodSymbol)parameter.ContainingSymbol;

            if (method.IsAbstract ||
                method.IsExtern ||
                method.PartialImplementationPart != null ||
                method.ContainingType.TypeKind == TypeKind.Interface)
            {
                return;
            }

            // We support initializing parameters, even when the containing member doesn't have a
            // body. This is useful for when the user is typing a new constructor and hasn't written
            // the body yet.
            var blockStatementOpt = GetBlockOperation(functionDeclaration, semanticModel, cancellationToken);

            // Ok.  Looks like a reasonable parameter to analyze.  Defer to subclass to
            // actually determine if there are any viable refactorings here.
            context.RegisterRefactorings(await GetRefactoringsAsync(
                                             document, parameter, functionDeclaration, method, blockStatementOpt, cancellationToken).ConfigureAwait(false));
        }
        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)
            {
                return;
            }

            var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);

            var token = root.FindToken(textSpan.Start);

            if (!token.Span.Contains(textSpan))
            {
                return;
            }

            var node = token.Parent;

            if (!node.IsKind(SyntaxKind.VariableDeclarator) ||
                !node.IsParentKind(SyntaxKind.VariableDeclaration) ||
                !node.Parent.IsParentKind(SyntaxKind.LocalDeclarationStatement))
            {
                return;
            }

            var variableDeclarator        = (VariableDeclaratorSyntax)node;
            var variableDeclaration       = (VariableDeclarationSyntax)variableDeclarator.Parent;
            var localDeclarationStatement = (LocalDeclarationStatementSyntax)variableDeclaration.Parent;

            if (variableDeclarator.Identifier != token ||
                variableDeclarator.Initializer == null ||
                variableDeclarator.Initializer.Value.IsMissing ||
                variableDeclarator.Initializer.Value.IsKind(SyntaxKind.StackAllocArrayCreationExpression))
            {
                return;
            }

            if (variableDeclaration.Type.Kind() == SyntaxKind.RefType)
            {
                // TODO: inlining ref returns:
                // https://github.com/dotnet/roslyn/issues/17132
                return;
            }

            if (localDeclarationStatement.ContainsDiagnostics)
            {
                return;
            }

            var references = await GetReferencesAsync(document, variableDeclarator, cancellationToken).ConfigureAwait(false);

            if (!references.Any())
            {
                return;
            }

            context.RegisterRefactoring(
                new MyCodeAction(
                    CSharpFeaturesResources.Inline_temporary_variable,
                    (c) => this.InlineTemporaryAsync(document, variableDeclarator, c)));
        }
        public override async Task ComputeRefactoringsAsync(CodeRefactoringContext context)
        {
            var(document, textSpan, cancellationToken) = context;
            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);
            }
        }
예제 #9
0
        static List <CodeAction> GetActions(CodeRefactoringProvider action, string input, out CSharpDiagnosticTestBase.TestWorkspace workspace, out Document doc, CSharpParseOptions parseOptions = null)
        {
            TextSpan selectedSpan;
            TextSpan markedSpan;
            string   text = ParseText(input, out selectedSpan, out markedSpan);

            workspace = new CSharpDiagnosticTestBase.TestWorkspace();
            var projectId  = ProjectId.CreateNewId();
            var documentId = DocumentId.CreateNewId(projectId);

            if (parseOptions == null)
            {
                parseOptions = new CSharpParseOptions(
                    LanguageVersion.CSharp6,
                    DocumentationMode.Diagnose | DocumentationMode.Parse,
                    SourceCodeKind.Regular,
                    ImmutableArray.Create("DEBUG", "TEST")
                    );
            }
            workspace.Options.WithChangedOption(CSharpFormattingOptions.NewLinesForBracesInControlBlocks, false);
            workspace.Open(ProjectInfo.Create(
                               projectId,
                               VersionStamp.Create(),
                               "TestProject",
                               "TestProject",
                               LanguageNames.CSharp,
                               null,
                               null,
                               new CSharpCompilationOptions(
                                   OutputKind.DynamicallyLinkedLibrary,
                                   "",
                                   "",
                                   "Script",
                                   null,
                                   OptimizationLevel.Debug,
                                   false,
                                   true
                                   ),
                               parseOptions,
                               new[] {
                DocumentInfo.Create(
                    documentId,
                    "a.cs",
                    null,
                    SourceCodeKind.Regular,
                    TextLoader.From(TextAndVersion.Create(SourceText.From(text), VersionStamp.Create()))
                    )
            },
                               null,
                               DiagnosticTestBase.DefaultMetadataReferences
                               )
                           );
            doc = workspace.CurrentSolution.GetProject(projectId).GetDocument(documentId);
            var actions = new List <CodeAction>();
            var context = new CodeRefactoringContext(doc, selectedSpan, actions.Add, default(CancellationToken));

            action.ComputeRefactoringsAsync(context).Wait();
            if (markedSpan.Start > 0)
            {
                foreach (var nra in actions.OfType <NRefactoryCodeAction>())
                {
                    Assert.AreEqual(markedSpan, nra.TextSpan, "Activation span does not match.");
                }
            }
            return(actions);
        }
        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);

            node = node.SkipArgument();

            // Get ancestor binary expressions and ensure they are string concatenation type.
            var ancestors = node.AncestorsAndSelf().Where(n => n is BinaryExpressionSyntax);

            // If there is no binary expressions then why are we here?
            if (!ancestors.Any())
            {
                return;
            }

            if (!(ancestors.Last().IsKind(SyntaxKind.AddExpression)))
            {
                return;
            }

            // Get highest binary expression available.
            node = ancestors.LastOrDefault(n => n is BinaryExpressionSyntax);

            // If there is not a binary expression, then there is no concatenaton to act on.
            if (node == null)
            {
                return;
            }

            var parts = GetParts(node as BinaryExpressionSyntax);

            // Ensure there are some parts, not sure how this could possibly happen, but better safe than sorry.
            if (parts.Count() <= 0)
            {
                return;
            }

            // Get all string type parts.
            var stringParts = parts.Where(p => IsStringExpression(p));

            // Ensure there is at least one string type part.
            if (!stringParts.Any())
            {
                return;
            }

            // Ensure strings are all verbatim or all not verbatim. It just gets ugly otherwise. ;p
            var verbatimParts = stringParts.Where(p => p.GetFirstToken().Text.StartsWith("@", System.StringComparison.OrdinalIgnoreCase));

            if (verbatimParts.Count() != 0 && verbatimParts.Count() != stringParts.Count())
            {
                return;
            }

            context.RegisterRefactoring(
                CodeActionFactory.Create(
                    node.Span,
                    DiagnosticSeverity.Info,
                    GettextCatalog.GetString("Use 'string.Format()'"),
                    t2 =>
            {
                var newRoot = root.ReplaceNode(node, ReplaceNode(node as BinaryExpressionSyntax));
                return(Task.FromResult(document.WithSyntaxRoot(newRoot)));
            }
                    )
                );

            return;
        }
예제 #11
0
 protected abstract Task <SyntaxNode> GetSelectedNodeAsync(CodeRefactoringContext context);
예제 #12
0
 protected abstract Task <SyntaxNode?> GetSelectedClassDeclarationAsync(CodeRefactoringContext context);
        public override async Task ComputeRefactoringsAsync(CodeRefactoringContext context)
        {
            var(document, textSpan, cancellationToken) = context;
            if (!textSpan.IsEmpty)
            {
                return;
            }

            if (document.Project.Solution.Workspace.Kind == WorkspaceKind.MiscellaneousFiles)
            {
                return;
            }

            var declaration = await GetDeclarationAsync(context).ConfigureAwait(false);

            if (declaration == null)
            {
                return;
            }

            Debug.Assert(declaration.IsKind(SyntaxKind.VariableDeclaration, SyntaxKind.ForEachStatement, SyntaxKind.DeclarationExpression));

            var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);

            var declaredType = FindAnalyzableType(declaration, semanticModel, cancellationToken);

            if (declaredType == null)
            {
                return;
            }

            if (declaredType.OverlapsHiddenPosition(cancellationToken))
            {
                return;
            }

            if (!declaredType.Span.IntersectsWith(textSpan.Start))
            {
                return;
            }

            var optionSet = await document.GetOptionsAsync(cancellationToken).ConfigureAwait(false);

            var typeStyle = AnalyzeTypeName(declaredType, semanticModel, optionSet, cancellationToken);

            if (typeStyle.IsStylePreferred && typeStyle.Severity != ReportDiagnostic.Suppress)
            {
                // the analyzer would handle this.  So we do not.
                return;
            }

            if (!typeStyle.CanConvert())
            {
                return;
            }

            context.RegisterRefactoring(
                new MyCodeAction(
                    Title,
                    c => UpdateDocumentAsync(document, declaredType, c)));
        }
예제 #14
0
        public override async Task ComputeRefactoringsAsync(CodeRefactoringContext context)
        {
            // Only offered when there isn't a selection.
            if (context.Span.Length > 0)
            {
                return;
            }

            var position          = context.Span.Start;
            var document          = context.Document;
            var cancellationToken = context.CancellationToken;

            var syntaxTree = await document.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false);

            var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);

            var token = root.FindToken(position);

            var parameterNode = GetParameterNode(token, position);

            if (parameterNode == null)
            {
                return;
            }

            var containingMember = parameterNode.FirstAncestorOrSelf <TMemberDeclarationSyntax>();

            if (containingMember == null)
            {
                return;
            }

            var syntaxFacts      = document.GetLanguageService <ISyntaxFactsService>();
            var parameterDefault = syntaxFacts.GetDefaultOfParameter(parameterNode);

            // Don't offer inside the "=initializer" of a parameter
            if (parameterDefault?.Span.Contains(position) == true)
            {
                return;
            }

            var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);

            var method = (IMethodSymbol)semanticModel.GetDeclaredSymbol(containingMember, cancellationToken);

            if (method.IsAbstract ||
                method.IsExtern ||
                method.PartialImplementationPart != null ||
                method.ContainingType.TypeKind == TypeKind.Interface)
            {
                return;
            }

            var parameter = (IParameterSymbol)semanticModel.GetDeclaredSymbol(parameterNode, cancellationToken);

            if (!method.Parameters.Contains(parameter))
            {
                return;
            }

            if (parameter.Name == "")
            {
                return;
            }

            // Only offered on method-like things that have a body (i.e. non-interface/non-abstract).
            var bodyOpt = GetBody(containingMember);

            // We support initializing parameters, even when the containing member doesn't have a
            // body. This is useful for when the user is typing a new constructor and hasn't written
            // the body yet.
            var blockStatementOpt = default(IBlockStatement);

            if (bodyOpt != null)
            {
                blockStatementOpt = GetOperation(semanticModel, bodyOpt, cancellationToken) as IBlockStatement;
                if (blockStatementOpt == null)
                {
                    return;
                }
            }

            // Ok.  Looks like a reasonable parameter to analyze.  Defer to subclass to
            // actually determine if there are any viable refactorings here.
            context.RegisterRefactorings(await GetRefactoringsAsync(
                                             document, parameter, containingMember, blockStatementOpt, cancellationToken).ConfigureAwait(false));
        }
        public override async Task ComputeRefactoringsAsync(CodeRefactoringContext context)
        {
            var document          = context.Document;
            var span              = context.Span;
            var cancellationToken = context.CancellationToken;
            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) &&
                !token.IsKind(SyntaxKind.AbstractKeyword) &&
                !token.IsKind(SyntaxKind.VirtualKeyword) &&
                !token.IsKind(SyntaxKind.ThisKeyword))
            {
                return;
            }
            var declaration = token.Parent as MemberDeclarationSyntax;

            if (token.IsKind(SyntaxKind.IdentifierToken))
            {
                if (token.Parent.Parent.IsKind(SyntaxKind.VariableDeclaration) &&
                    token.Parent.Parent.Parent.IsKind(SyntaxKind.EventFieldDeclaration))
                {
                    declaration = token.Parent.Parent.Parent as MemberDeclarationSyntax;
                }
            }
            if (declaration == null ||
                declaration is BaseTypeDeclarationSyntax ||
                declaration is ConstructorDeclarationSyntax ||
                declaration is DestructorDeclarationSyntax ||
                declaration.GetBodies().IsEmpty())
            {
                return;
            }
            var modifiers = declaration.GetModifiers();

            if (modifiers.Any(m => m.IsKind(SyntaxKind.OverrideKeyword)))
            {
                return;
            }

            TypeDeclarationSyntax enclosingTypeDeclaration = declaration.Ancestors().OfType <TypeDeclarationSyntax>().FirstOrDefault();

            if (enclosingTypeDeclaration == null || enclosingTypeDeclaration is InterfaceDeclarationSyntax)
            {
                return;
            }

            var explicitInterface = declaration.GetExplicitInterfaceSpecifierSyntax();

            if (explicitInterface != null)
            {
                return;
            }

            //			if (selectedNode != node.NameToken) {
            //				if ((node is EventDeclaration && node is CustomEventDeclaration || selectedNode.Role != Roles.Identifier) &&
            //					selectedNode.Role != IndexerDeclaration.ThisKeywordRole) {
            //					var modToken = selectedNode as CSharpModifierToken;
            //					if (modToken == null || (modToken.Modifier & (Modifiers.Abstract | Modifiers.Virtual)) == 0)
            //						yield break;
            //				} else {
            //					if (!(node is EventDeclaration || node is CustomEventDeclaration) && selectedNode.Parent != node)
            //						yield break;
            //				}
            //			}
            //			if (!node.GetChildByRole(EntityDeclaration.PrivateImplementationTypeRole).IsNull)
            //				yield break;
            //
            if (enclosingTypeDeclaration.Modifiers.Any(m => m.IsKind(SyntaxKind.AbstractKeyword)))
            {
                if (modifiers.Any(m => m.IsKind(SyntaxKind.AbstractKeyword)))
                {
                    context.RegisterRefactoring(CodeActionFactory.Create(
                                                    token.Span,
                                                    DiagnosticSeverity.Info,
                                                    GettextCatalog.GetString("To non-abstract"),
                                                    t2 =>
                    {
                        var newRoot = root.ReplaceNode((SyntaxNode)declaration, ImplementAbstractDeclaration(declaration).WithAdditionalAnnotations(Formatter.Annotation));
                        return(Task.FromResult(document.WithSyntaxRoot(newRoot)));
                    }
                                                    )
                                                );
                }
                else
                {
                    if (CheckBody(declaration))
                    {
                        context.RegisterRefactoring(CodeActionFactory.Create(
                                                        token.Span,
                                                        DiagnosticSeverity.Info,
                                                        GettextCatalog.GetString("To abstract"),
                                                        t2 =>
                        {
                            var newRoot = root.ReplaceNode((SyntaxNode)declaration, MakeAbstractDeclaration(declaration).WithAdditionalAnnotations(Formatter.Annotation));
                            return(Task.FromResult(document.WithSyntaxRoot(newRoot)));
                        }
                                                        )
                                                    );
                    }
                }
            }

            if (modifiers.Any(m => m.IsKind(SyntaxKind.VirtualKeyword)))
            {
                context.RegisterRefactoring(CodeActionFactory.Create(
                                                token.Span,
                                                DiagnosticSeverity.Info,
                                                GettextCatalog.GetString("To non-virtual"),
                                                t2 =>
                {
                    var newRoot = root.ReplaceNode((SyntaxNode)declaration, RemoveVirtualModifier(declaration));
                    return(Task.FromResult(document.WithSyntaxRoot(newRoot)));
                }
                                                )
                                            );
            }
            else
            {
                if (modifiers.Any(m => m.IsKind(SyntaxKind.AbstractKeyword)))
                {
                    context.RegisterRefactoring(CodeActionFactory.Create(
                                                    token.Span,
                                                    DiagnosticSeverity.Info,
                                                    GettextCatalog.GetString("To virtual"),
                                                    t2 =>
                    {
                        var newRoot = root.ReplaceNode((SyntaxNode)declaration, ImplementAbstractDeclaration(declaration, true).WithAdditionalAnnotations(Formatter.Annotation));
                        return(Task.FromResult(document.WithSyntaxRoot(newRoot)));
                    }
                                                    )
                                                );
                }
                else if (!enclosingTypeDeclaration.Modifiers.Any(m => m.IsKind(SyntaxKind.StaticKeyword)))
                {
                    context.RegisterRefactoring(CodeActionFactory.Create(
                                                    token.Span,
                                                    DiagnosticSeverity.Info,
                                                    GettextCatalog.GetString("To virtual"),
                                                    t2 =>
                    {
                        var newRoot = root.ReplaceNode((SyntaxNode)declaration, AddModifier(declaration, SyntaxKind.VirtualKeyword).WithAdditionalAnnotations(Formatter.Annotation));
                        return(Task.FromResult(document.WithSyntaxRoot(newRoot)));
                    }
                                                    )
                                                );
                }
            }
        }
 public sealed override Task ComputeRefactoringsAsync(CodeRefactoringContext context)
 => _codeStyleProvider.ComputeRefactoringsAsync(context);
예제 #17
0
        private async Task <ExtractClassWithDialogCodeAction?> TryGetClassActionAsync(CodeRefactoringContext context, IExtractClassOptionsService optionsService)
        {
            var selectedClassNode = await GetSelectedClassDeclarationAsync(context).ConfigureAwait(false);

            if (selectedClassNode is null)
            {
                return(null);
            }

            var(document, span, cancellationToken) = context;

            var semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false);

            var originalType = semanticModel.GetDeclaredSymbol(selectedClassNode, cancellationToken) as INamedTypeSymbol;

            if (originalType is null)
            {
                return(null);
            }

            return(new ExtractClassWithDialogCodeAction(document, span, optionsService, originalType, selectedClassNode));
        }
예제 #18
0
        private async Task <ExtractClassWithDialogCodeAction?> TryGetMemberActionAsync(CodeRefactoringContext context, IExtractClassOptionsService optionsService)
        {
            var selectedMemberNode = await GetSelectedNodeAsync(context).ConfigureAwait(false);

            if (selectedMemberNode is null)
            {
                return(null);
            }

            var(document, span, cancellationToken) = context;
            var semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false);

            var selectedMember = semanticModel.GetDeclaredSymbol(selectedMemberNode, cancellationToken);

            if (selectedMember is null || selectedMember.ContainingType is null)
            {
                return(null);
            }

            // Use same logic as pull members up for determining if a selected member
            // is valid to be moved into a base
            if (!MemberAndDestinationValidator.IsMemberValid(selectedMember))
            {
                return(null);
            }

            var containingType = selectedMember.ContainingType;

            // Can't extract to a new type if there's already a base. Maybe
            // in the future we could inject a new type inbetween base and
            // current
            if (containingType.BaseType?.SpecialType != SpecialType.System_Object)
            {
                return(null);
            }

            var syntaxFacts = document.GetRequiredLanguageService <ISyntaxFactsService>();
            var containingTypeDeclarationNode = selectedMemberNode.FirstAncestorOrSelf <SyntaxNode>(syntaxFacts.IsTypeDeclaration);

            return(new ExtractClassWithDialogCodeAction(document, span, optionsService, containingType, containingTypeDeclarationNode !, selectedMember));
        }
예제 #19
0
        public override async Task ComputeRefactoringsAsync(CodeRefactoringContext context)
        {
            var(document, textSpan, cancellationToken) = context;
            var(tupleExprOrTypeNode, tupleType)        = await TryGetTupleInfoAsync(
                document, textSpan, 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));
        }
예제 #20
0
        public override async Task ComputeRefactoringsAsync(CodeRefactoringContext context)
        {
            var(document, classDeclarationSyntax) = await context.FindSyntaxForCurrentSpan <ClassDeclarationSyntax>();

            if (document == null || classDeclarationSyntax == null)
            {
                return;
            }

            if (ClassDeclarationSyntaxAnalysis.IsPartial(classDeclarationSyntax) ||
                ClassDeclarationSyntaxAnalysis.IsStatic(classDeclarationSyntax))
            {
                return;
            }

            var(atMostOneNonTrivialConstructor, nonTrivialConstructorCandidate) =
                ClassDeclarationSyntaxAnalysis.HasAtMostOneNoneTrivialConstructor(classDeclarationSyntax);

            if (!atMostOneNonTrivialConstructor)
            {
                return;
            }

            var(_, propertyDeclaration) = await context.FindSyntaxForCurrentSpan <PropertyDeclarationSyntax>();

            var(_, fieldDeclaration) = await context.FindSyntaxForCurrentSpan <FieldDeclarationSyntax>();

            // TODO: Skip properties like string Prop => "something";
            if ((fieldDeclaration == null && propertyDeclaration == null) ||
                (fieldDeclaration != null && fieldDeclaration.IsStatic()) ||
                (propertyDeclaration != null && propertyDeclaration.IsStatic()))
            {
                return;
            }

            var(_, fieldVariableDeclaration) = await context.FindVariableDeclaratorForCurrentSpan();

            if (fieldDeclaration != null && fieldVariableDeclaration == null)
            {
                return;
            }

            switch (nonTrivialConstructorCandidate)
            {
            case ConstructorDeclarationSyntax candidate:
            {
                var cancellationToken = context.CancellationToken;
                var semanticModel     = await document.GetSemanticModelAsync(cancellationToken);

                HandleCandidate(semanticModel, candidate, propertyDeclaration, fieldDeclaration, fieldVariableDeclaration);
            }
            break;

            case null:
            {
                var trivialConstructor = ClassDeclarationSyntaxAnalysis.GetConstructors(classDeclarationSyntax)?.FirstOrDefault();
                if (trivialConstructor != null)
                {
                    var cancellationToken = context.CancellationToken;
                    var semanticModel     = await document.GetSemanticModelAsync(cancellationToken);

                    HandleCandidate(semanticModel, trivialConstructor, propertyDeclaration, fieldDeclaration, fieldVariableDeclaration);
                }
                else
                {
                    // TODO: register add constructor refactoring
                }
            }

            break;
            }

            void HandleCandidate(
                SemanticModel model,
                ConstructorDeclarationSyntax candidate,
                PropertyDeclarationSyntax pDeclaration,
                FieldDeclarationSyntax fDeclaration,
                VariableDeclaratorSyntax variableDeclarator)
            {
                bool    isProp            = pDeclaration != null;
                var     constructorSymbol = model.GetDeclaredSymbol(candidate) as IMethodSymbol;
                ISymbol memberSymbol      = isProp ?
                                            model.GetDeclaredSymbol(pDeclaration)
                    :  model.GetDeclaredSymbol(variableDeclarator);

                if (constructorSymbol == null || memberSymbol == null)
                {
                    return;
                }

                var analyser = new ConstructorPropertyRelationshipAnalyser(
                    isProp ? Array.Empty <IFieldSymbol>() : new[] { memberSymbol as IFieldSymbol },
                    isProp ? new[] { memberSymbol as IPropertySymbol } : Array.Empty <IPropertySymbol>());

                var result             = analyser.Analyze(model, candidate);
                var assignmentResult   = result.GetResult(memberSymbol);
                AnalysedDeclaration ad = isProp ?
                                         (AnalysedDeclaration) new PropertyDeclaration(memberSymbol as IPropertySymbol, pDeclaration)
                    : new FieldDeclaration(memberSymbol as IFieldSymbol, fDeclaration, variableDeclarator);

                switch (assignmentResult)
                {
                case AssignmentExpressionAnalyserResult r:
                    // register remove as assignment exists
                    context.RegisterRefactoring(
                        new DelegateCodeAction(
                            $"Remove {ad.Identifier.ValueText}",
                            (c) => RefactoringActions.RemoveParameter(
                                document,
                                classDeclarationSyntax,
                                ad,
                                candidate,
                                c)));
                    break;

                case EmptyAssignmentAnalyserResult _:
                    // register add to constructor
                    context.RegisterRefactoring(
                        new DelegateCodeAction(
                            $"Add {ad.Identifier.ValueText} to constructor",
                            (c) => RefactoringActions.AddParameterToConstructor(
                                document,
                                classDeclarationSyntax,
                                ad,
                                constructorSymbol,
                                candidate,
                                c)));
                    break;

                case MultipleAssignments _:
                case ParsingError _:
                default:
                    // Something went wrong so it might be better to do nothing.
                    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.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 root = await document.GetSyntaxRootAsync(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 => ReplaceToken(document, root, numericToken, value, text, suffix)),
                    numericToken.Span);
            }
        }
        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 syntaxNode      = root.FindNode(span);
            var returnStatement = syntaxNode as ReturnStatementSyntax;

            if (returnStatement != null)
            {
                if (!(returnStatement.Expression is ConditionalExpressionSyntax) && !(returnStatement.Expression is BinaryExpressionSyntax))
                {
                    return;
                }
                if (returnStatement.Expression is BinaryExpressionSyntax && !returnStatement.Expression.IsKind(SyntaxKind.CoalesceExpression))
                {
                    return;
                }

                context.RegisterRefactoring(
                    CodeActionFactory.Create(
                        span,
                        DiagnosticSeverity.Info,
                        GettextCatalog.GetString("Replace with 'if' statement"),
                        t2 =>
                {
                    StatementSyntax statement         = null;
                    ReturnStatementSyntax returnAfter = null;
                    if (returnStatement.Expression is ConditionalExpressionSyntax)
                    {
                        var condition = (ConditionalExpressionSyntax)returnStatement.Expression;
                        statement     = SyntaxFactory.IfStatement(condition.Condition, SyntaxFactory.ReturnStatement(condition.WhenTrue));
                        returnAfter   = SyntaxFactory.ReturnStatement(condition.WhenFalse);
                    }
                    else
                    {
                        var bOp = returnStatement.Expression as BinaryExpressionSyntax;
                        if (bOp != null && bOp.IsKind(SyntaxKind.CoalesceExpression))
                        {
                            statement   = SyntaxFactory.IfStatement(SyntaxFactory.BinaryExpression(SyntaxKind.NotEqualsExpression, bOp.Left, SyntaxFactory.LiteralExpression(SyntaxKind.NullLiteralExpression)), SyntaxFactory.ReturnStatement(bOp.Left));
                            returnAfter = SyntaxFactory.ReturnStatement(bOp.Right);
                        }
                    }

                    var newRoot = root.ReplaceNode((SyntaxNode)returnStatement, new SyntaxNode[] {
                        statement.WithAdditionalAnnotations(Formatter.Annotation).WithLeadingTrivia(returnStatement.GetLeadingTrivia()),
                        returnAfter.WithAdditionalAnnotations(Formatter.Annotation)
                    });
                    return(Task.FromResult(document.WithSyntaxRoot(newRoot)));
                }
                        )
                    );
            }

            var assignExpr = syntaxNode as AssignmentExpressionSyntax;

            if (assignExpr != null)
            {
                if (assignExpr.Right.IsKind(SyntaxKind.ConditionalExpression))
                {
                    context.RegisterRefactoring(
                        CodeActionFactory.Create(
                            span,
                            DiagnosticSeverity.Info,
                            GettextCatalog.GetString("Replace with 'if' statement"),
                            t2 =>
                    {
                        var ifStatement = CreateForConditionalExpression(assignExpr, (ConditionalExpressionSyntax)assignExpr.Right);
                        return(Task.FromResult(document.WithSyntaxRoot(root.ReplaceNode((SyntaxNode)assignExpr.Parent, ifStatement.WithAdditionalAnnotations(Formatter.Annotation)))));
                    }
                            )
                        );
                }
                if (assignExpr.Right.IsKind(SyntaxKind.CoalesceExpression))
                {
                    context.RegisterRefactoring(
                        CodeActionFactory.Create(
                            span,
                            DiagnosticSeverity.Info,
                            GettextCatalog.GetString("Replace with 'if' statement"),
                            t2 =>
                    {
                        var ifStatement = CreateForNullCoalescingExpression(assignExpr, (BinaryExpressionSyntax)assignExpr.Right);
                        return(Task.FromResult(document.WithSyntaxRoot(root.ReplaceNode((SyntaxNode)assignExpr.Parent, ifStatement.WithAdditionalAnnotations(Formatter.Annotation)))));
                    }
                            )
                        );
                }
            }
        }
예제 #23
0
        public override async Task ComputeRefactoringsAsync(CodeRefactoringContext context)
        {
            var(document, span, cancellationToken) = context;
            if (!span.IsEmpty)
            {
                return;
            }

            var tree = await document.GetRequiredSyntaxTreeAsync(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 root = await tree.GetRootAsync(cancellationToken).ConfigureAwait(false);

            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;
                }
            }
        }
        private static async Task <(TTypeDeclarationSyntax type, CodeActionPriority priority)?> GetRelevantTypeFromHeaderAsync(CodeRefactoringContext context)
        {
            var type = await context.TryGetRelevantNodeAsync <TTypeDeclarationSyntax>().ConfigureAwait(false);

            if (type is null)
            {
                return(null);
            }

            return(type, CodeActionPriority.Low);
        }
예제 #25
0
        public async Task <SyntaxNode?> GetMethodDeclarationAsync(CodeRefactoringContext context)
#pragma warning restore CA1822 // Mark members as static
        => await context.TryGetRelevantNodeAsync <TMethodDeclarationSyntax>().ConfigureAwait(false);
        private static async Task <(TTypeDeclarationSyntax type, CodeActionPriority priority)?> GetRelevantTypeFromMethodAsync(CodeRefactoringContext context)
        {
            var(document, _, cancellationToken) = context;
            var method = await context.TryGetRelevantNodeAsync <TMethodDeclarationSyntax>().ConfigureAwait(false);

            if (method == null)
            {
                return(null);
            }

            var semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false);

            var methodSymbol = (IMethodSymbol)semanticModel.GetRequiredDeclaredSymbol(method, cancellationToken);

            var isDebuggerDisplayMethod = IsDebuggerDisplayMethod(methodSymbol);

            if (!isDebuggerDisplayMethod && !IsToStringMethod(methodSymbol))
            {
                return(null);
            }

            // Show the feature if we're on a ToString or GetDebuggerDisplay method. For the former,
            // have this be low-pri so we don't override more important light-bulb options.
            var typeDecl = method.FirstAncestorOrSelf <TTypeDeclarationSyntax>();

            if (typeDecl == null)
            {
                return(null);
            }

            var priority = isDebuggerDisplayMethod ? CodeActionPriority.Medium : CodeActionPriority.Low;

            return(typeDecl, priority);
        }
예제 #27
0
        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 newField = SyntaxFactory.FieldDeclaration(
                        SyntaxFactory.VariableDeclaration(
                            parameter.Type,
                            SyntaxFactory.SingletonSeparatedList <VariableDeclaratorSyntax>(SyntaxFactory.VariableDeclarator(parameter.Identifier)))
                        ).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(parameter.Identifier)),
                            SyntaxFactory.IdentifierName(parameter.Identifier)
                            )
                        ).WithAdditionalAnnotations(Formatter.Annotation);

                    root        = root.TrackNodes(ctor);
                    var newRoot = root.InsertNodesBefore(root.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 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)));
                }));
            }
        }
        public override async Task ComputeRefactoringsAsync(CodeRefactoringContext context)
        {
            var(document, textSpan, cancellationToken) = context;
            var possibleExpressions = await context.GetRelevantNodesAsync <TExpressionSyntax>().ConfigureAwait(false);

            var syntaxFacts   = document.GetLanguageService <ISyntaxFactsService>();
            var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);

            // let's take the largest (last) StringConcat we can given current textSpan
            var top = possibleExpressions
                      .Where(expr => IsStringConcat(syntaxFacts, expr, semanticModel, cancellationToken))
                      .LastOrDefault();

            if (top == null)
            {
                return;
            }

            // if there is a const keyword, the refactoring shouldn't show because interpolated string is not const string
            var declarator = top.FirstAncestorOrSelf <SyntaxNode>(syntaxFacts.IsVariableDeclarator);

            if (declarator != null)
            {
                var generator = SyntaxGenerator.GetGenerator(document);
                if (generator.GetModifiers(declarator).IsConst)
                {
                    return;
                }
            }

            // Currently we can concatenate only full subtrees. Therefore we can't support arbitrary selection. We could
            // theoretically support selecting the selections that correspond to full sub-trees (e.g. prefixes of
            // correct length but from UX point of view that it would feel arbitrary).
            // Thus, we only support selection that takes the whole topmost expression. It breaks some leniency around under-selection
            // but it's the best solution so far.
            if (CodeRefactoringHelpers.IsNodeUnderselected(top, textSpan) ||
                IsStringConcat(syntaxFacts, top.Parent, semanticModel, cancellationToken))
            {
                return;
            }

            // Now walk down the concatenation collecting all the pieces that we are
            // concatenating.
            var pieces = new List <SyntaxNode>();

            CollectPiecesDown(syntaxFacts, pieces, top, semanticModel, cancellationToken);

            var stringLiterals = pieces
                                 .Where(x => syntaxFacts.IsStringLiteralExpression(x) || syntaxFacts.IsCharacterLiteralExpression(x))
                                 .ToImmutableArray();

            // If the entire expression is just concatenated strings, then don't offer to
            // make an interpolated string.  The user likely manually split this for
            // readability.
            if (stringLiterals.Length == pieces.Count)
            {
                return;
            }

            var isVerbatimStringLiteral = false;

            if (stringLiterals.Length > 0)
            {
                // Make sure that all the string tokens we're concatenating are the same type
                // of string literal.  i.e. if we have an expression like: @" "" " + " \r\n "
                // then we don't merge this.  We don't want to be munging different types of
                // escape sequences in these strings, so we only support combining the string
                // tokens if they're all the same type.
                var firstStringToken = stringLiterals[0].GetFirstToken();
                isVerbatimStringLiteral = syntaxFacts.IsVerbatimStringLiteral(firstStringToken);
                if (stringLiterals.Any(
                        lit => isVerbatimStringLiteral != syntaxFacts.IsVerbatimStringLiteral(lit.GetFirstToken())))
                {
                    return;
                }
            }

            var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);

            var interpolatedString = CreateInterpolatedString(document, isVerbatimStringLiteral, pieces);

            context.RegisterRefactoring(
                new MyCodeAction(
                    _ => UpdateDocumentAsync(document, root, top, interpolatedString)),
                top.Span);
        }
 private static void PostDecrementToPostIncrement(CodeRefactoringContext context, PostfixUnaryExpressionSyntax postfixUnaryExpression)
 {
     context.RegisterRefactoring(
         "Convert to increment operator",
         cancellationToken => ChangePostDecrementToPostIncrementAsync(context.Document, postfixUnaryExpression, cancellationToken));
 }
예제 #31
0
        private async Task HandleNonSelectionAsync(CodeRefactoringContext context)
        {
            var (document, textSpan, cancellationToken) = context;

            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, out var typeDeclaration) &&
                !syntaxFacts.IsBetweenTypeMembers(sourceText, root, textSpan.Start, out typeDeclaration))
            {
                return;
            }

            var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);

            // Only supported on classes/structs.
            var containingType = semanticModel.GetDeclaredSymbol(typeDeclaration) as INamedTypeSymbol;
            if (containingType?.TypeKind != TypeKind.Class && containingType?.TypeKind != TypeKind.Struct)
            {
                return;
            }

            // No overrides in static classes.
            if (containingType.IsStatic)
            {
                return;
            }

            // Find all the possible instance fields/properties.  If there are any, then
            // show a dialog to the user to select the ones they want.
            var viableMembers = containingType
                .GetBaseTypesAndThis()
                .Reverse()
                .SelectAccessibleMembers<ISymbol>(containingType)
                .Where(IsReadableInstanceFieldOrProperty)
                .ToImmutableArray();

            if (viableMembers.Length == 0)
            {
                return;
            }

            GetExistingMemberInfo(
                containingType, out var hasEquals, out var hasGetHashCode);

            var pickMembersOptions = ArrayBuilder<PickMembersOption>.GetInstance();

            var equatableTypeOpt = semanticModel.Compilation.GetTypeByMetadataName(typeof(IEquatable<>).FullName);
            if (equatableTypeOpt != null)
            {
                var constructedType = equatableTypeOpt.Construct(containingType);
                if (!containingType.AllInterfaces.Contains(constructedType))
                {
                    var options = await document.GetOptionsAsync(cancellationToken).ConfigureAwait(false);
                    var value = options.GetOption(GenerateEqualsAndGetHashCodeFromMembersOptions.ImplementIEquatable);

                    var displayName = constructedType.ToDisplayString(new SymbolDisplayFormat(
                        typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameOnly,
                        genericsOptions: SymbolDisplayGenericsOptions.IncludeTypeParameters));

                    pickMembersOptions.Add(new PickMembersOption(
                        ImplementIEquatableId,
                        string.Format(FeaturesResources.Implement_0, displayName),
                        value));
                }
            }

            if (!HasOperators(containingType))
            {
                var options = await document.GetOptionsAsync(cancellationToken).ConfigureAwait(false);
                var value = options.GetOption(GenerateEqualsAndGetHashCodeFromMembersOptions.GenerateOperators);
                pickMembersOptions.Add(new PickMembersOption(
                    GenerateOperatorsId,
                    FeaturesResources.Generate_operators,
                    value));
            }

            var actions = CreateActions(
                document, textSpan, typeDeclaration, containingType,
                viableMembers, pickMembersOptions.ToImmutableAndFree(),
                hasEquals, hasGetHashCode,
                withDialog: true);

            context.RegisterRefactorings(actions);
        }
예제 #32
0
 protected override Task <SyntaxNode?> GetSelectedNodeAsync(CodeRefactoringContext context) =>
 NodeSelectionHelpers.GetSelectedDeclarationOrVariableAsync(context);
 public sealed override async Task ComputeRefactoringsAsync(CodeRefactoringContext context)
 {
     var service = context.Document.GetLanguageService<AbstractEncapsulateFieldService>();
     var actions = await service.GetEncapsulateFieldCodeActionsAsync(context.Document, context.Span, context.CancellationToken).ConfigureAwait(false);
     context.RegisterRefactorings(actions);
 }
예제 #34
0
 public override Task ComputeRefactoringsAsync(CodeRefactoringContext context)
 {
     throw new Exception($"Exception thrown from ComputeRefactoringsAsync in {nameof(ExceptionInCodeActions)}");
 }
예제 #35
0
 private CodeAction CreateAction(CodeRefactoringContext context, Scope scope)
 => new MyCodeAction(GetTitle(scope), c => ConvertToStructAsync(context.Document, context.Span, scope, c));
		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);
		}