microsoft/healthcare-shared-components

Public

mirrored from https://github.com/microsoft/healthcare-shared-componentsAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
1.0.0-1.0.79

Branches

Tags

  • No tags available.
0Branches0Tags
Go to file
Add file
Code

Clone

HTTPS

Download ZIP

tools/Microsoft.Health.Extensions.BuildTimeCodeGenerator/IndentedStringBuilderGenerator.cs

121lines · modecode

1// -------------------------------------------------------------------------------------------------
2// Copyright (c) Microsoft Corporation. All rights reserved.
3// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information.
4// -------------------------------------------------------------------------------------------------
5
6using System;
7using System.Linq;
8using System.Text;
9using Microsoft.CodeAnalysis;
10using Microsoft.CodeAnalysis.CSharp;
11using Microsoft.CodeAnalysis.CSharp.Syntax;
12using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
13
14namespace Microsoft.Health.Extensions.BuildTimeCodeGenerator
15{
16 /// <summary>
17 /// Generates a class that wraps <see cref="StringBuilder"/> and provides methods for prefixing lines with an indent level.
18 /// </summary>
19 internal class IndentedStringBuilderGenerator : ICodeGenerator
20 {
21 public IndentedStringBuilderGenerator(string[] args)
22 {
23 }
24
25 (MemberDeclarationSyntax[], UsingDirectiveSyntax[]) ICodeGenerator.Generate(string typeName)
26 {
27 // start off by generating a class that has the same API shape as StringBuilder and that forwards calls to an inner
28 // StringBuilder. Reuse DelegatingInterfaceImplementationGenerator even though StringBuilder is not an interface.
29 // We'll patch things up after.
30
31 var delegatingGenerator = new DelegatingInterfaceImplementationGenerator(
32 typeModifiers: TokenList(Token(SyntaxKind.PublicKeyword), Token(SyntaxKind.PartialKeyword)),
33 constructorModifiers: TokenList(Token(SyntaxKind.PublicKeyword)),
34 typeof(StringBuilder));
35
36 (MemberDeclarationSyntax[] declarations, UsingDirectiveSyntax[] usings) = delegatingGenerator.Generate(typeName);
37
38 SyntaxNode syntaxNode = new Rewriter().Visit(declarations.Single().SyntaxTree.GetRoot());
39
40 return (new[] { (MemberDeclarationSyntax)syntaxNode }, usings);
41 }
42
43 private class Rewriter : CSharpSyntaxRewriter
44 {
45 public override SyntaxNode VisitClassDeclaration(ClassDeclarationSyntax node)
46 {
47 return base.VisitClassDeclaration(node.WithBaseList(null));
48 }
49
50 public override SyntaxNode VisitPropertyDeclaration(PropertyDeclarationSyntax node)
51 {
52 return node
53 .WithExplicitInterfaceSpecifier(null)
54 .AddModifiers(Token(SyntaxKind.PublicKeyword));
55 }
56
57 public override SyntaxNode VisitIndexerDeclaration(IndexerDeclarationSyntax node)
58 {
59 return node
60 .WithExplicitInterfaceSpecifier(null)
61 .AddModifiers(Token(SyntaxKind.PublicKeyword));
62 }
63
64 public override SyntaxNode VisitMethodDeclaration(MethodDeclarationSyntax node)
65 {
66 if (node.ReturnType is PointerTypeSyntax || node.ParameterList.Parameters.Any(p => p.Type is PointerTypeSyntax))
67 {
68 // skip unsafe methods
69 return IncompleteMember();
70 }
71
72 node = node
73 .WithExplicitInterfaceSpecifier(null)
74 .AddModifiers(Token(SyntaxKind.PublicKeyword));
75
76 string methodName = node.Identifier.ToString();
77 if (IsDeclaredOnObject(node))
78 {
79 if (methodName == nameof(object.GetType))
80 {
81 // don't implement object.GetType()
82 return IncompleteMember();
83 }
84
85 node = node.AddModifiers(Token(SyntaxKind.OverrideKeyword));
86 }
87
88 if (node.ReturnType.ToString() == typeof(StringBuilder).FullName)
89 {
90 // return this instead of the inner StringBuilder that is returned by the inner call.
91 node = node.WithReturnType(IdentifierName("IndentedStringBuilder"));
92 ExpressionSyntax invocation = ((ReturnStatementSyntax)node.Body.Statements.Single()).Expression;
93 node = node.WithBody(Block(ExpressionStatement(invocation), ReturnStatement(ThisExpression())));
94 }
95
96 if (methodName.StartsWith("Append", StringComparison.OrdinalIgnoreCase))
97 {
98 // Add a call to AppendIndent at the start of the body
99 BlockSyntax body = node.Body.WithStatements(node.Body.Statements.Insert(0, ExpressionStatement(InvocationExpression(IdentifierName("AppendIndent")))));
100
101 if (methodName.EndsWith("Line", StringComparison.OrdinalIgnoreCase))
102 {
103 // before returning, set _indentPending to true
104 ExpressionStatementSyntax updatePendingStatement = ExpressionStatement(AssignmentExpression(SyntaxKind.SimpleAssignmentExpression, IdentifierName("_indentPending"), LiteralExpression(SyntaxKind.TrueLiteralExpression)));
105
106 body = body.InsertNodesBefore(body.Statements.Single(s => s is ReturnStatementSyntax), new SyntaxNode[] { updatePendingStatement });
107 }
108
109 node = node.WithBody(body);
110 }
111
112 return node;
113 }
114
115 private static bool IsDeclaredOnObject(SyntaxNode node)
116 {
117 return node.GetAnnotations(DelegatingInterfaceImplementationGenerator.DeclaringTypeKind).Single().Data == typeof(object).FullName;
118 }
119 }
120 }
121}