using System;
using System.Collections.Generic;
using System.Linq;
using CTA.Rules.Config;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.VisualBasic;
using Microsoft.CodeAnalysis.VisualBasic.Syntax;
using Microsoft.CodeAnalysis.Editing;
using CTA.Rules.Actions.ActionHelpers;
namespace CTA.Rules.Actions.VisualBasic
{
///
/// List of actions that can run on Method Declarations
///
public class MethodBlockActions
{
public Func GetAddCommentAction(string comment, string dontUseCTAPrefix = null)
{
MethodBlockSyntax AddComment(SyntaxGenerator syntaxGenerator, MethodBlockSyntax node)
{
return (MethodBlockSyntax)CommentHelper.AddVBComment(node, comment, dontUseCTAPrefix);
}
return AddComment;
}
public Func GetAppendExpressionAction(string expression)
{
// TODO: This will add an expression at the bottom of a method body, in the future we should add granularity for where to add the expression within a method body
Func appendExpression = (SyntaxGenerator syntaxGenerator, MethodBlockSyntax node) =>
{
StatementSyntax statementExpression = expression.Contains("Await")
? ParseAwaitExpression(expression)
: SyntaxFactory.ParseExecutableStatement(expression);
if (!statementExpression.FullSpan.IsEmpty)
{
node = node.AddStatements(statementExpression).NormalizeWhitespace();
}
return node;
};
return appendExpression;
}
public Func GetChangeMethodNameAction(string newMethodName)
{
MethodBlockSyntax ChangeMethodName(SyntaxGenerator syntaxGenerator, MethodBlockSyntax node)
{
var newMethodStatement = node.SubOrFunctionStatement.WithIdentifier(SyntaxFactory.Identifier(newMethodName));
var newMethodNode = node.WithSubOrFunctionStatement(newMethodStatement).NormalizeWhitespace();
return newMethodNode;
}
return ChangeMethodName;
}
public Func GetChangeMethodToReturnTaskTypeAction()
{
MethodBlockSyntax ChangeMethodToReturnTaskType(SyntaxGenerator syntaxGenerator, MethodBlockSyntax node)
{
var methodStatement = node.SubOrFunctionStatement;
var oldAsClause = methodStatement.AsClause;
if (methodStatement.IsKind(SyntaxKind.SubStatement))
{
// if sub, convert to function and return task
var functionStatement = SyntaxFactory.FunctionStatement(methodStatement.Identifier);
functionStatement =
functionStatement.WithAsClause(
SyntaxFactory.SimpleAsClause(SyntaxFactory.IdentifierName("Task")))
.WithModifiers(methodStatement.Modifiers.Add(SyntaxFactory.Token(SyntaxKind.AsyncKeyword)));
var newNode = SyntaxFactory.MethodBlock(SyntaxKind.FunctionBlock,
functionStatement, node.Statements, SyntaxFactory.EndFunctionStatement());
return newNode.NormalizeWhitespace();
}
// already function, need to wrap return in task
var genericName =
SyntaxFactory.GenericName("Task", SyntaxFactory.TypeArgumentList(oldAsClause.Type));
methodStatement = methodStatement.WithAsClause(SyntaxFactory.SimpleAsClause(genericName))
.WithModifiers(methodStatement.Modifiers.Add(SyntaxFactory.Token(SyntaxKind.AsyncKeyword)));
return node.WithSubOrFunctionStatement(methodStatement).NormalizeWhitespace();
}
return ChangeMethodToReturnTaskType;
}
public Func GetRemoveMethodParametersAction()
{
MethodBlockSyntax RemoveMethodParametersAction(SyntaxGenerator syntaxGenerator, MethodBlockSyntax node)
{
var parameters = new List();
var newMethodStatement =
node.SubOrFunctionStatement
.WithParameterList(SyntaxFactory.ParameterList(SyntaxFactory.SeparatedList(parameters)))
.NormalizeWhitespace();
return node.WithSubOrFunctionStatement(newMethodStatement).NormalizeWhitespace();
}
return RemoveMethodParametersAction;
}
public Func GetAddParametersToMethodAction(string types, string identifiers)
{
MethodBlockSyntax AddParametersToMethodAction(SyntaxGenerator syntaxGenerator, MethodBlockSyntax node)
{
var newMethodStatement = node.SubOrFunctionStatement;
if (!string.IsNullOrWhiteSpace(identifiers) && !string.IsNullOrWhiteSpace(types))
{
var identifiersArray = identifiers.Split(',', StringSplitOptions.RemoveEmptyEntries);
var typesArray = types.Split(',', StringSplitOptions.RemoveEmptyEntries);
if (identifiersArray.Length == typesArray.Length)
{
var parameters = new List();
for (var i = 0; i < identifiersArray.Length; i++)
{
parameters.Add(SyntaxFactory
.Parameter(SyntaxFactory.ModifiedIdentifier(identifiersArray[i]))
.WithAsClause(
SyntaxFactory.SimpleAsClause(SyntaxFactory.ParseTypeName(typesArray[i])))
.NormalizeWhitespace());
}
var separatedSyntaxList = SyntaxFactory.SeparatedList(parameters);
newMethodStatement =
newMethodStatement.WithParameterList(SyntaxFactory.ParameterList(separatedSyntaxList))
.NormalizeWhitespace();
}
}
return node.WithSubOrFunctionStatement(newMethodStatement).NormalizeWhitespace();
}
return AddParametersToMethodAction;
}
public Func GetCommentMethodAction(
string comment = null,
string dontUseCTAPrefix = null)
{
MethodBlockSyntax CommentMethodAction(SyntaxGenerator syntaxGenerator, MethodBlockSyntax node)
{
var bodyStatments = node.Statements;
var newBody = new SyntaxTriviaList();
foreach (var statement in bodyStatments)
{
var newLine = SyntaxFactory.SyntaxTrivia(SyntaxKind.CommentTrivia, @$"' {statement.ToFullString()}");
newBody = newBody.Add(newLine).Add(SyntaxFactory.SyntaxTrivia(SyntaxKind.EndOfLineTrivia, ""));
}
var endStatement = node.EndSubOrFunctionStatement.WithLeadingTrivia(newBody);
var newMethodNode = node.WithEndSubOrFunctionStatement(endStatement)
.WithStatements(new SyntaxList());
if (!string.IsNullOrWhiteSpace(comment))
{
var addCommentsToMethodFunc = GetAddCommentAction(comment, dontUseCTAPrefix);
return addCommentsToMethodFunc(syntaxGenerator, newMethodNode);
}
return newMethodNode.NormalizeWhitespace();
}
return CommentMethodAction;
}
public Func GetAddExpressionToMethodAction(string expression)
{
MethodBlockSyntax AddExpressionToMethodAction(SyntaxGenerator syntaxGenerator, MethodBlockSyntax node)
{
var newMethodNode = node;
var parsedExpression = expression.Contains("Await")
? ParseAwaitExpression(expression)
: SyntaxFactory.ParseExecutableStatement(expression);
if (!parsedExpression.FullSpan.IsEmpty)
{
var body = node.Statements;
int returnIndex = body.IndexOf(s => s.IsKind(SyntaxKind.ReturnStatement));
if (returnIndex >= 0)
{
// insert new statement before return
body = body.Insert(returnIndex, parsedExpression);
newMethodNode = node.WithStatements(body);
}
else
{
newMethodNode = node.AddStatements(parsedExpression);
}
}
return newMethodNode.NormalizeWhitespace();
}
return AddExpressionToMethodAction;
}
private StatementSyntax ParseAwaitExpression(string expression)
{
expression = expression.Replace("Await", "", StringComparison.OrdinalIgnoreCase);
var parsedExpression = SyntaxFactory.ParseExpression(expression);
var awaitExpression = SyntaxFactory.AwaitExpression(parsedExpression);
return SyntaxFactory.ExpressionStatement(awaitExpression);
}
}
}