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); }
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); } }
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; }
protected abstract Task <SyntaxNode> GetSelectedNodeAsync(CodeRefactoringContext context);
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))); }
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);
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)); }
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)); }
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)); }
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))))); } ) ); } } }
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); }
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); }
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)); }
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); }
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); }
public override Task ComputeRefactoringsAsync(CodeRefactoringContext context) { throw new Exception($"Exception thrown from ComputeRefactoringsAsync in {nameof(ExceptionInCodeActions)}"); }
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); }