示例#1
0
        public async Task <CodeAction> IntroduceVariableAsync(
            Document document,
            TextSpan textSpan,
            CodeCleanupOptions options,
            CancellationToken cancellationToken)
        {
            using (Logger.LogBlock(FunctionId.Refactoring_IntroduceVariable, cancellationToken))
            {
                var semanticDocument = await SemanticDocument.CreateAsync(document, cancellationToken).ConfigureAwait(false);

                var state = await State.GenerateAsync((TService)this, semanticDocument, options, textSpan, cancellationToken).ConfigureAwait(false);

                if (state != null)
                {
                    var(title, actions) = CreateActions(state, cancellationToken);
                    if (actions.Length > 0)
                    {
                        // We may end up creating a lot of viable code actions for the selected
                        // piece of code.  Create a top level code action so that we don't overwhelm
                        // the light bulb if there are a lot of other options in the list.  Set
                        // the code action as 'inlinable' so that if the lightbulb is not cluttered
                        // then the nested items can just be lifted into it, giving the user fast
                        // access to them.
                        return(CodeActionWithNestedActions.Create(title, actions, isInlinable: true));
                    }
                }

                return(null);
            }
        }
        public override async Task ComputeRefactoringsAsync(CodeRefactoringContext context)
        {
            // Currently support to pull field, method, event, property and indexer up,
            // constructor, operator and finalizer are excluded.
            var document          = context.Document;
            var cancellationToken = context.CancellationToken;
            var semanticModel     = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);

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

            var selectedMemberNode = await GetMatchedNodeAsync(document, context.Span, cancellationToken).ConfigureAwait(false);

            if (selectedMemberNode == null)
            {
                return;
            }

            var selectedMember = semanticModel.GetDeclaredSymbol(selectedMemberNode);

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

            if (!MemberAndDestinationValidator.IsMemberValid(selectedMember))
            {
                return;
            }

            if (!IsSelectionValid(context.Span, selectedMemberNode))
            {
                return;
            }

            var allDestinations = FindAllValidDestinations(
                selectedMember,
                document.Project.Solution,
                cancellationToken);

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

            var allActions = allDestinations.Select(destination => MembersPuller.TryComputeCodeAction(document, selectedMember, destination))
                             .WhereNotNull().Concat(new PullMemberUpWithDialogCodeAction(document, selectedMember, _service))
                             .ToImmutableArray();

            var nestedCodeAction = new CodeActionWithNestedActions(
                string.Format(FeaturesResources.Pull_0_up, selectedMember.ToNameDisplayString()),
                allActions, isInlinable: true);

            context.RegisterRefactoring(nestedCodeAction);
        }
示例#3
0
        /// <summary>
        /// Creates a <see cref="CodeAction"/> representing a group of code actions.
        /// </summary>
        /// <param name="title">Title of the <see cref="CodeAction"/> group.</param>
        /// <param name="nestedActions">The code actions within the group.</param>
        /// <param name="isInlinable"><see langword="true"/> to allow inlining the members of the group into the parent;
        /// otherwise, <see langword="false"/> to require that this group appear as a group with nested actions.</param>
        public static CodeAction Create(string title, ImmutableArray <CodeAction> nestedActions, bool isInlinable)
        {
            if (title is null)
            {
                throw new ArgumentNullException(nameof(title));
            }

            if (nestedActions == null)
            {
                throw new ArgumentNullException(nameof(nestedActions));
            }

            return(CodeActionWithNestedActions.Create(title, nestedActions, isInlinable));
        }
示例#4
0
        public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context)
        {
            var cancellationToken = context.CancellationToken;
            var document          = context.Document;
            var syntaxFacts       = document.GetRequiredLanguageService <ISyntaxFactsService>();
            var root = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false);

            // Innermost: We are looking for an IdentifierName. IdentifierName is sometimes at the same span as its parent (e.g. SimpleBaseTypeSyntax).
            var diagnosticNode = root.FindNode(context.Span, getInnermostNodeForTie: true);

            if (!syntaxFacts.IsIdentifierName(diagnosticNode))
            {
                return;
            }

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

            var symbolInfo = semanticModel.GetSymbolInfo(diagnosticNode, cancellationToken);

            if (SymbolCandidatesContainsSupportedSymbols(symbolInfo))
            {
                var addImportService = document.GetRequiredLanguageService <IAddImportsService>();
                var syntaxGenerator  = document.GetRequiredLanguageService <SyntaxGenerator>();
                var codeGenerator    = document.GetRequiredLanguageService <ICodeGenerationService>();
                var compilation      = semanticModel.Compilation;
                var preferences      = await CodeGenerationPreferences.FromDocumentAsync(document, cancellationToken).ConfigureAwait(false);

                var allowInHiddenRegions = document.CanAddImportsInHiddenRegions();
                var codeActionsBuilder   = ImmutableArray.CreateBuilder <CodeAction>(symbolInfo.CandidateSymbols.Length);
                foreach (var symbol in symbolInfo.CandidateSymbols.Cast <ITypeSymbol>())
                {
                    var typeName = symbol.Name;
                    var codeActionPreviewText = GetTextPreviewOfChange(typeName, symbol);
                    codeActionsBuilder.Add(new MyCodeAction(codeActionPreviewText, c =>
                    {
                        var aliasDirective = syntaxGenerator.AliasImportDeclaration(typeName, symbol);
                        var newRoot        = addImportService.AddImport(compilation, root, diagnosticNode, aliasDirective, syntaxGenerator, preferences, allowInHiddenRegions, cancellationToken);
                        return(Task.FromResult(document.WithSyntaxRoot(newRoot)));
                    }));
                }

                var groupingTitle      = string.Format(FeaturesResources.Alias_ambiguous_type_0, diagnosticNode.ToString());
                var groupingCodeAction = new CodeActionWithNestedActions(groupingTitle, codeActionsBuilder.ToImmutable(), isInlinable: true);
                context.RegisterCodeFix(groupingCodeAction, context.Diagnostics.First());
            }
        }
        public sealed override async Task ComputeRefactoringsAsync(CodeRefactoringContext context)
        {
            var(document, textSpan, cancellationToken) = context;
            if (document.Project.Solution.Workspace.Kind == WorkspaceKind.MiscellaneousFiles)
            {
                return;
            }

            var expression = await document.TryGetRelevantNodeAsync <TExpressionSyntax>(textSpan, cancellationToken).ConfigureAwait(false);

            if (expression == null || CodeRefactoringHelpers.IsNodeUnderselected(expression, textSpan))
            {
                return;
            }

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

            var expressionType = semanticModel.GetTypeInfo(expression, cancellationToken).Type;

            if (expressionType is null or IErrorTypeSymbol)
            {
                return;
            }

            var syntaxFacts = document.GetRequiredLanguageService <ISyntaxFactsService>();

            // Need to special case for expressions that are contained within a parameter
            // because it is technically "contained" within a method, but an expression in a parameter does not make
            // sense to introduce.
            var parameterNode = expression.FirstAncestorOrSelf <SyntaxNode>(node => syntaxFacts.IsParameter(node));

            if (parameterNode is not null)
            {
                return;
            }

            // Need to special case for highlighting of method types because they are also "contained" within a method,
            // but it does not make sense to introduce a parameter in that case.
            if (syntaxFacts.IsInNamespaceOrTypeContext(expression))
            {
                return;
            }

            // Need to special case for expressions whose direct parent is a MemberAccessExpression since they will
            // never introduce a parameter that makes sense in that case.
            if (syntaxFacts.IsNameOfAnyMemberAccessExpression(expression))
            {
                return;
            }

            var generator        = SyntaxGenerator.GetGenerator(document);
            var containingMethod = expression.FirstAncestorOrSelf <SyntaxNode>(node => generator.GetParameterListNode(node) is not null);

            if (containingMethod is null)
            {
                return;
            }

            var containingSymbol = semanticModel.GetDeclaredSymbol(containingMethod, cancellationToken);

            if (containingSymbol is not IMethodSymbol methodSymbol)
            {
                return;
            }

            var expressionSymbol = semanticModel.GetSymbolInfo(expression, cancellationToken).Symbol;

            if (expressionSymbol is IParameterSymbol parameterSymbol && parameterSymbol.ContainingSymbol.Equals(containingSymbol))
            {
                return;
            }

            // Code actions for trampoline and overloads will not be offered if the method is a constructor.
            // Code actions for overloads will not be offered if the method if the method is a local function.
            var methodKind = methodSymbol.MethodKind;

            if (methodKind is not(MethodKind.Ordinary or MethodKind.LocalFunction or MethodKind.Constructor))
            {
                return;
            }

            if (IsDestructor(methodSymbol))
            {
                return;
            }

            var actions = await GetActionsAsync(document, expression, methodSymbol, containingMethod, context.Options, cancellationToken).ConfigureAwait(false);

            if (actions is null)
            {
                return;
            }

            var singleLineExpression = syntaxFacts.ConvertToSingleLine(expression);
            var nodeString           = singleLineExpression.ToString();

            if (actions.Value.actions.Length > 0)
            {
                context.RegisterRefactoring(CodeActionWithNestedActions.Create(
                                                string.Format(FeaturesResources.Introduce_parameter_for_0, nodeString), actions.Value.actions, isInlinable: false, priority: CodeActionPriority.Low), textSpan);
            }

            if (actions.Value.actionsAllOccurrences.Length > 0)
            {
                context.RegisterRefactoring(CodeActionWithNestedActions.Create(
                                                string.Format(FeaturesResources.Introduce_parameter_for_all_occurrences_of_0, nodeString), actions.Value.actionsAllOccurrences, isInlinable: false,
                                                priority: CodeActionPriority.Low), textSpan);
            }
        }