Created
November 13, 2016 21:06
-
-
Save tiesmaster/4a2a81b83c45dcd336fb5757b1eaab56 to your computer and use it in GitHub Desktop.
so-40578806 "what-is-the-canonical-way-to-convert-a-methoddeclarationsyntax-to-a-constructord" support
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<Query Kind="Program"> | |
<NuGetReference>Microsoft.CodeAnalysis</NuGetReference> | |
<NuGetReference>Microsoft.CodeAnalysis.CSharp</NuGetReference> | |
<Namespace>Microsoft.CodeAnalysis</Namespace> | |
<Namespace>Microsoft.CodeAnalysis.CSharp.Syntax</Namespace> | |
<Namespace>Microsoft.CodeAnalysis.MSBuild</Namespace> | |
<Namespace>Microsoft.CodeAnalysis.CSharp</Namespace> | |
<Namespace>Microsoft.CodeAnalysis.Formatting</Namespace> | |
<Namespace>Microsoft.CodeAnalysis.Text</Namespace> | |
</Query> | |
void Main() | |
{ | |
var tree = CSharpSyntaxTree.ParseText( | |
@"public class Thing | |
{ | |
protected override void Configure() => CreateMap<InvoiceDto, Invoice>().ConstructUsing(MapInvoice); | |
public void Hoi() | |
{ | |
// Hoi | |
} | |
}"); | |
var root = tree.GetRootAsync().Result; | |
root.ToFullString().Dump("old root"); | |
var methodNode = root.DescendantNodes().OfType<MethodDeclarationSyntax>().First(); | |
var convertedNode = ConvertProtectedOverrideToConstructor(methodNode); | |
var newRoot = root.ReplaceNode(methodNode, convertedNode); | |
newRoot.ToFullString().Dump("new root"); | |
} | |
SyntaxNode ConvertProtectedOverrideToConstructor(MethodDeclarationSyntax oldMethodNode) | |
{ | |
var classIdentifier = oldMethodNode.Ancestors().OfType<ClassDeclarationSyntax>().Single().Identifier; | |
var constructorIdentifier = classIdentifier.NormalizeWhitespace(); | |
var newBody = oldMethodNode.Body; | |
var newParameterList = oldMethodNode.ParameterList; | |
var expressionBody = oldMethodNode.ExpressionBody; | |
if (newBody == null) | |
{ | |
var oldMethodTrailingTrivia = oldMethodNode.GetTrailingTrivia(); | |
newParameterList = newParameterList.WithTrailingTrivia(oldMethodTrailingTrivia); | |
var expressionBodyAsStatement = SyntaxFactory.ExpressionStatement(expressionBody.Expression); | |
newBody = SyntaxFactory.Block(expressionBodyAsStatement).NormalizeWhitespace(); | |
var baseIndentationForMethod = oldMethodNode.GetLeadingTrivia().Last(); | |
newBody = (BlockSyntax)newBody.AddIndentationFromTrivia(baseIndentationForMethod); | |
} | |
return SyntaxFactory | |
.ConstructorDeclaration(constructorIdentifier) | |
.WithModifiers(SyntaxFactory.TokenList(SyntaxFactory.Token(SyntaxKind.PublicKeyword))) | |
.NormalizeWhitespace() | |
.WithAttributeLists(oldMethodNode.AttributeLists) | |
.WithParameterList(newParameterList) | |
.WithBody(newBody) | |
.WithTriviaFrom(oldMethodNode); | |
} | |
public static class SyntaxNodeManipulationExtensions | |
{ | |
public static SyntaxNode AddIndentationFromTrivia(this SyntaxNode oldNode, SyntaxTrivia indentation) | |
{ | |
var baseIndentation = indentation.ToString(); | |
var tokensToLineStartPositionsDictionary = oldNode.GetNonEmptyLinePositions() | |
.GroupBy(lineStart => oldNode.FindTriviaIncludingZeroLength(lineStart).Token) | |
.ToDictionary(group => group.Key, group => group.ToList()); | |
return oldNode.ReplaceTokens(tokensToLineStartPositionsDictionary.Keys, (originalToken, rewrittenToken) => | |
{ | |
var triviasToReplace = tokensToLineStartPositionsDictionary[originalToken].Select(lineStart => originalToken.Parent.FindTriviaIncludingZeroLength(lineStart)); | |
return originalToken.ReplaceTrivia(triviasToReplace, (originalTrivia, rewrittenTrivia) | |
=> SyntaxFactory.Whitespace(baseIndentation + originalTrivia)); | |
}); | |
} | |
} | |
public static class SyntaxNodeExtensions | |
{ | |
public static SyntaxTrivia FindTriviaIncludingZeroLength(this SyntaxNode node, int position) | |
=> node.DescendantTrivia().Single(trivia => trivia.SpanStart == position); | |
public static IEnumerable<int> GetNonEmptyLinePositions(this SyntaxNode node) | |
{ | |
return | |
from line in node.GetText().Lines | |
where !line.IsEmpty() | |
let offset = node.FullSpan.Start | |
select offset + line.Start; | |
} | |
} | |
public static class TextLineExtensions | |
{ | |
public static bool IsEmpty(this TextLine line) | |
=> line.Start == line.End; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment