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); }
/// <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)); }
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); } }