internal static void AddField(this DocumentEditor editor, TypeDeclarationSyntax containingType, FieldDeclarationSyntax field) { FieldDeclarationSyntax existing = null; foreach (var member in containingType.Members) { if (member is FieldDeclarationSyntax fieldDeclaration) { if (IsInsertBefore(fieldDeclaration)) { editor.InsertBefore(fieldDeclaration, field); return; } existing = fieldDeclaration; continue; } editor.InsertBefore(member, field); return; } if (existing != null) { editor.InsertAfter(existing, field); } else { editor.AddMember(containingType, field); } }
public void AddNSubstituteUsing() { CompilationUnitSyntax root = _editor.GetChangedRoot() as CompilationUnitSyntax; if (!root.Usings.Any(x => x.Name.GetText().ToString() == "NSubstitute")) { var newUsing = SyntaxFactory.UsingDirective(SyntaxFactory.IdentifierName("NSubstitute")); _editor.InsertBefore((_editor.OriginalRoot as CompilationUnitSyntax).Usings.FirstOrDefault(), newUsing); } }
/// <inheritdoc /> protected override void ChangePropertyCore(DocumentEditor editor, PropertyDeclarationSyntax propertySyntax) { var ownerType = (propertySyntax.Parent as ClassDeclarationSyntax)?.Identifier.ValueText; if (propertySyntax.AccessorList is null || ownerType is null) { return; } // 生成可通知属性的类型/名称/字段名称。 var propertyType = propertySyntax.Type; propertyType = propertyType is NullableTypeSyntax nullableTypeSyntax ? nullableTypeSyntax.ElementType : propertyType; var propertyName = propertySyntax.Identifier.ValueText; var attachedPropertyName = $"{propertyName}Property"; // 增加字段。 editor.InsertBefore(propertySyntax, new SyntaxNode[] { // private Type _field; SyntaxFactory.FieldDeclaration( new SyntaxList <AttributeListSyntax>(), new SyntaxTokenList( SyntaxFactory.Token(SyntaxKind.PrivateKeyword), SyntaxFactory.Token(SyntaxKind.StaticKeyword), SyntaxFactory.Token(SyntaxKind.ReadOnlyKeyword)), SyntaxFactory.VariableDeclaration( SyntaxFactory.ParseTypeName("System.Windows.DependencyProperty"), SyntaxFactory.SeparatedList(new[]
private void InsertNewVariableDeclaration( BinaryExpressionSyntax asExpression, SyntaxToken newIdentifier, SyntaxNode nodeLocation, DocumentEditor editor, ref bool variableAlreadyExtracted) { if (variableAlreadyExtracted) { return; } var newEqualsClause = SyntaxFactory.EqualsValueClause(asExpression); var newDeclarator = SyntaxFactory.VariableDeclarator(newIdentifier.WithAdditionalAnnotations(RenameAnnotation.Create()), null, newEqualsClause); var newDeclaration = SyntaxFactory.VariableDeclaration(SyntaxFactory.IdentifierName("var"), SyntaxFactory.SeparatedList(new[] { newDeclarator })); var newLocal = SyntaxFactory.LocalDeclarationStatement(newDeclaration).WithAdditionalAnnotations(Formatter.Annotation); // If we are in an else statement, we have to add the new local before the initial if-statement. e.g.: // if(o is int) { } // else if(o is string) { } // If we are currently handling the second statement, we have to add the local before the first // However because there can be multiple chained if-else statements, we have to go up to the first one and add it there. nodeLocation = GetOuterIfStatement(nodeLocation); editor.InsertBefore(nodeLocation, newLocal); variableAlreadyExtracted = true; }
/// <summary> /// Move <paramref name="toMove"></paramref> before <paramref name="statement">.</paramref>. /// </summary> /// <param name="editor">The <see cref="DocumentEditor"/>.</param> /// <param name="toMove">The <see cref="StatementSyntax"/> to move.</param> /// <param name="statement">The <see cref="StatementSyntax"/>.</param> /// <returns>The <see cref="DocumentEditor"/> that was passed in.</returns> public static DocumentEditor MoveBefore(this DocumentEditor editor, StatementSyntax toMove, StatementSyntax statement) { if (editor is null) { throw new ArgumentNullException(nameof(editor)); } if (toMove is null) { throw new ArgumentNullException(nameof(toMove)); } if (statement is null) { throw new ArgumentNullException(nameof(statement)); } editor.RemoveNode(toMove); editor.InsertBefore(statement, ToMove()); return(editor); StatementSyntax ToMove() { if (statement.GetLastToken().IsKind(SyntaxKind.CloseBraceToken)) { return(toMove.WithoutLeadingLineFeed()); } return(toMove); } }
private static void AddCheckIfDifferent(DocumentEditor editor, AssignmentExpressionSyntax assignment, CancellationToken cancellationToken) { var statementSyntax = assignment.FirstAncestorOrSelf <ExpressionStatementSyntax>(); if (statementSyntax == null) { return; } var type = editor.SemanticModel.GetTypeInfoSafe(assignment.Left, cancellationToken).Type; var code = StringBuilderPool.Borrow() .AppendLine($"if ({Snippet.EqualityCheck(type, "value", assignment.Left.ToString(), editor.SemanticModel)})") .AppendLine("{") .AppendLine(" return;") .AppendLine("}") .AppendLine() .Return(); var ifReturn = SyntaxFactory.ParseStatement(code) .WithSimplifiedNames() .WithLeadingElasticLineFeed() .WithTrailingElasticLineFeed() .WithAdditionalAnnotations(Formatter.Annotation); editor.InsertBefore(statementSyntax, ifReturn); }
private static void DisposeInVirtualDisposeMethod(DocumentEditor editor, ISymbol memberSymbol, MethodDeclarationSyntax disposeMethodDeclaration, CancellationToken cancellationToken) { var disposeStatement = Snippet.DisposeStatement(memberSymbol, editor.SemanticModel, cancellationToken); if (TryFindIfDisposing(disposeMethodDeclaration, out var ifDisposing)) { if (ifDisposing.Statement is BlockSyntax block) { var statements = block.Statements.Add(disposeStatement); var newBlock = block.WithStatements(statements); editor.ReplaceNode(block, newBlock); } else if (ifDisposing.Statement is StatementSyntax statement) { editor.ReplaceNode( ifDisposing, ifDisposing.WithStatement(SyntaxFactory.Block(statement, disposeStatement))); } else { editor.ReplaceNode( ifDisposing, ifDisposing.WithStatement(SyntaxFactory.Block(disposeStatement))); } } else if (disposeMethodDeclaration.Body is BlockSyntax block) { ifDisposing = SyntaxFactory.IfStatement( SyntaxFactory.IdentifierName(disposeMethodDeclaration.ParameterList.Parameters[0].Identifier), SyntaxFactory.Block(disposeStatement)); if (DisposeMethod.TryFindBaseCall(disposeMethodDeclaration, editor.SemanticModel, cancellationToken, out var baseCall)) { if (baseCall.TryFirstAncestor(out ExpressionStatementSyntax expressionStatement)) { editor.InsertBefore(expressionStatement, ifDisposing); } } else { editor.ReplaceNode( block, block.AddStatements(ifDisposing)); } } }
/// <inheritdoc /> protected override void ChangePropertyCore(DocumentEditor editor, PropertyDeclarationSyntax propertySyntax) { if (propertySyntax.AccessorList is null) { return; } // 生成可通知属性的类型/名称/字段名称。 var propertyType = propertySyntax.Type; var propertyName = propertySyntax.Identifier.ValueText; var fieldName = $"_{char.ToLower(propertyName[0], CultureInfo.InvariantCulture)}{propertyName.Substring(1)}"; // 增加字段。 editor.InsertBefore(propertySyntax, new SyntaxNode[] { // private Type _field; SyntaxFactory.FieldDeclaration( new SyntaxList <AttributeListSyntax>(), new SyntaxTokenList(SyntaxFactory.Token(SyntaxKind.PrivateKeyword)), SyntaxFactory.VariableDeclaration( propertyType, SyntaxFactory.SeparatedList(new[] { SyntaxFactory.VariableDeclarator( SyntaxFactory.Identifier(fieldName) ) }) ), SyntaxFactory.Token(SyntaxKind.SemicolonToken)) }); // 替换 get/set。 editor.ReplaceNode( propertySyntax, SyntaxFactory.ParseMemberDeclaration( $@"{propertySyntax.AttributeLists.ToFullString()}{propertySyntax.Modifiers.ToFullString()}{propertySyntax.Type.ToFullString()}{propertySyntax.Identifier.ToFullString()} {{ get => {fieldName}; set => SetValue(ref {fieldName}, value); }}") ! .WithAdditionalAnnotations(new SyntaxAnnotation[] { Simplifier.Annotation, Formatter.Annotation }) ); }
public static async Task AddMissingNamespaces(this DocumentEditor editor, NamespaceSet namespaces, IReadOnlyList <ImportedNamespace> importedNamespaces, Func <SyntaxNode, IEnumerable <SyntaxNode>, SyntaxNode> addUsings, CancellationToken ct) { var orderedNamespaces = namespaces.OrderBy((x => x.ToDisplayString())).ToArray(); if (orderedNamespaces.Length == 0) { return; } var g = editor.Generator; if (importedNamespaces == null || importedNamespaces.Count == 0) { var root = await editor.GetChangedDocument().GetSyntaxRootAsync(ct); var newRoot = addUsings(root, orderedNamespaces.Select(x => g.GenerateNamespaceImportDeclaration(x))); editor.ReplaceNode(root, newRoot); return; } for (int i = 0; i < orderedNamespaces.Length; i++) { var namespaceSymbol = orderedNamespaces[i]; var index = namespaceSymbol.GetIndexToInsertBefore(importedNamespaces); if (index < 0) { continue; } if (index == importedNamespaces.Count) { var lastNode = importedNamespaces.LastOrDefault().SyntaxNode; Append(editor, lastNode, orderedNamespaces, i); return; } var newNode = g.GenerateNamespaceImportDeclaration(namespaceSymbol); editor.InsertBefore(importedNamespaces[index].SyntaxNode, newNode); } }
/// <summary> /// Move <paramref name="toMove"></paramref> before <paramref name="member">.</paramref>. /// </summary> /// <param name="editor">The <see cref="DocumentEditor"/>.</param> /// <param name="toMove">The <see cref="MemberDeclarationSyntax"/> to move.</param> /// <param name="member">The <see cref="MemberDeclarationSyntax"/>.</param> /// <returns>The <see cref="DocumentEditor"/> that was passed in.</returns> public static DocumentEditor MoveBefore(this DocumentEditor editor, MemberDeclarationSyntax toMove, MemberDeclarationSyntax member) { if (editor is null) { throw new ArgumentNullException(nameof(editor)); } if (toMove is null) { throw new ArgumentNullException(nameof(toMove)); } if (member is null) { throw new ArgumentNullException(nameof(member)); } editor.RemoveNode(toMove); editor.InsertBefore(member, ToMove()); editor.ReplaceNode(member, Member()); return(editor); MemberDeclarationSyntax ToMove() { if (toMove.Parent is TypeDeclarationSyntax typeDeclaration) { return(toMove.AdjustLeadingNewLine(typeDeclaration.Members.ElementAtOrDefault(typeDeclaration.Members.IndexOf(member) - 1))); } return(toMove); } MemberDeclarationSyntax Member() { return(member.AdjustLeadingNewLine(toMove)); } }
private async Task <Document> ConvertToGeneratedDllImport( Document doc, MethodDeclarationSyntax methodSyntax, IMethodSymbol methodSymbol, AttributeData dllImportAttr, INamedTypeSymbol generatedDllImportAttrType, bool usePreprocessorDefines, CancellationToken cancellationToken) { DocumentEditor editor = await DocumentEditor.CreateAsync(doc, cancellationToken).ConfigureAwait(false); SyntaxGenerator generator = editor.Generator; var dllImportSyntax = (AttributeSyntax)dllImportAttr !.ApplicationSyntaxReference !.GetSyntax(cancellationToken); // Create GeneratedDllImport attribute based on the DllImport attribute SyntaxNode generatedDllImportSyntax = GetGeneratedDllImportAttribute( editor, generator, dllImportSyntax, methodSymbol.GetDllImportData() !, generatedDllImportAttrType, out SyntaxNode? unmanagedCallConvAttributeMaybe); // Add annotation about potential behavioural and compatibility changes generatedDllImportSyntax = generatedDllImportSyntax.WithAdditionalAnnotations( WarningAnnotation.Create(string.Format(Resources.ConvertToGeneratedDllImportWarning, "[TODO] Documentation link"))); // Replace DllImport with GeneratedDllImport SyntaxNode generatedDeclaration = generator.ReplaceNode(methodSyntax, dllImportSyntax, generatedDllImportSyntax); if (unmanagedCallConvAttributeMaybe is not null) { generatedDeclaration = generator.AddAttributes(generatedDeclaration, unmanagedCallConvAttributeMaybe); } // Replace extern keyword with partial keyword generatedDeclaration = generator.WithModifiers( generatedDeclaration, generator.GetModifiers(methodSyntax) .WithIsExtern(false) .WithPartial(true)); if (!usePreprocessorDefines) { // Replace the original method with the updated one editor.ReplaceNode(methodSyntax, generatedDeclaration); } else { // #if DLLIMPORTGENERATOR_ENABLED generatedDeclaration = generatedDeclaration.WithLeadingTrivia( generatedDeclaration.GetLeadingTrivia() .AddRange(new[] { SyntaxFactory.Trivia(SyntaxFactory.IfDirectiveTrivia(SyntaxFactory.IdentifierName("DLLIMPORTGENERATOR_ENABLED"), isActive: true, branchTaken: true, conditionValue: true)), SyntaxFactory.ElasticMarker })); // #else generatedDeclaration = generatedDeclaration.WithTrailingTrivia( generatedDeclaration.GetTrailingTrivia() .AddRange(new[] { SyntaxFactory.Trivia(SyntaxFactory.ElseDirectiveTrivia(isActive: false, branchTaken: false)), SyntaxFactory.ElasticMarker })); // Remove existing leading trivia - it will be on the GeneratedDllImport method MethodDeclarationSyntax updatedDeclaration = methodSyntax.WithLeadingTrivia(); // #endif updatedDeclaration = updatedDeclaration.WithTrailingTrivia( methodSyntax.GetTrailingTrivia() .AddRange(new[] { SyntaxFactory.Trivia(SyntaxFactory.EndIfDirectiveTrivia(isActive: true)), SyntaxFactory.ElasticMarker })); // Add the GeneratedDllImport method editor.InsertBefore(methodSyntax, generatedDeclaration); // Replace the original method with the updated DllImport method editor.ReplaceNode(methodSyntax, updatedDeclaration); } return(editor.GetChangedDocument()); }
private static void MakeWithBackingFieldNotifyWhenValueChanges(DocumentEditor editor, PropertyDeclarationSyntax propertyDeclaration, IMethodSymbol invoker, SemanticModel semanticModel, CancellationToken cancellationToken) { var classDeclaration = propertyDeclaration.FirstAncestorOrSelf <ClassDeclarationSyntax>(); if (classDeclaration == null) { return; } if (propertyDeclaration.TryGetSetter(out var setter)) { if (setter.ExpressionBody != null && IsSimpleAssignmentOnly(propertyDeclaration, out _, out _, out var assignment, out _)) { var property = semanticModel.GetDeclaredSymbolSafe(propertyDeclaration, cancellationToken); var underscoreFields = CodeStyle.UnderscoreFields(semanticModel); var code = StringBuilderPool.Borrow() .AppendLine($"public Type PropertyName") .AppendLine("{") .AppendLine($" get => {assignment.Left};") .AppendLine() .AppendLine(" set") .AppendLine(" {") .AppendLine($" if ({Snippet.EqualityCheck(property.Type, "value", assignment.Left.ToString(), semanticModel)})") .AppendLine(" {") .AppendLine($" return;") .AppendLine(" }") .AppendLine() .AppendLine($" {assignment};") .AppendLine($" {Snippet.OnPropertyChanged(invoker, property.Name, underscoreFields)}") .AppendLine(" }") .AppendLine("}") .Return(); var template = ParseProperty(code); editor.ReplaceNode( setter, (x, _) => { var old = (AccessorDeclarationSyntax)x; return(old.WithBody(template.Setter().Body) .WithExpressionBody(null) .WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.None))); }); editor.FormatNode(propertyDeclaration); } if (setter.Body?.Statements.Count == 1 && IsSimpleAssignmentOnly(propertyDeclaration, out _, out var statement, out assignment, out _)) { var property = semanticModel.GetDeclaredSymbolSafe(propertyDeclaration, cancellationToken); var code = StringBuilderPool.Borrow() .AppendLine($" if ({Snippet.EqualityCheck(property.Type, "value", assignment.Left.ToString(), semanticModel)})") .AppendLine(" {") .AppendLine($" return;") .AppendLine(" }") .AppendLine() .Return(); var ifStatement = SyntaxFactory.ParseStatement(code) .WithSimplifiedNames() .WithLeadingElasticLineFeed() .WithTrailingElasticLineFeed() .WithAdditionalAnnotations(Formatter.Annotation); editor.InsertBefore( statement, ifStatement); var underscoreFields = CodeStyle.UnderscoreFields(semanticModel); var notifyStatement = SyntaxFactory .ParseStatement( Snippet.OnPropertyChanged(invoker, property.Name, underscoreFields)) .WithSimplifiedNames() .WithLeadingElasticLineFeed() .WithTrailingElasticLineFeed() .WithAdditionalAnnotations(Formatter.Annotation); editor.InsertAfter(statement, notifyStatement); editor.FormatNode(propertyDeclaration); } } }