For example, I have the following documentation comment in my C# code file:
/// add k+5
I want to REPLACE it with the node
_tst.AddElement(k+5);
How can I do it using C#/Roslyn? I've found how to add this line, but didn't found how to replace. My code that adds the node:
public static MethodDeclarationSyntax getChangedNode(MethodDeclarationSyntax method)
{
var newmethod = method;
var TestEntryArgName = "_tst";
/* Adding _tst.AddElement(i); */
foreach (var s in newmethod.Body.DescendantNodes())
{
SyntaxTrivia st = SyntaxFactory.SyntaxTrivia(SyntaxKind.WhitespaceTrivia, " ");
bool fl = false;
bool before = true;
var lt = s.GetLeadingTrivia();
foreach (var triviaEntry in lt)
{
if (triviaEntry.Kind() == SyntaxKind.SingleLineDocumentationCommentTrivia)
{
fl = true;
st = triviaEntry;
break;
}
}
if (!fl)
{
lt = s.GetTrailingTrivia();
before = false;
foreach (var triviaEntry in lt)
{
if (triviaEntry.Kind() == SyntaxKind.SingleLineDocumentationCommentTrivia)
{
fl = true;
st = triviaEntry;
break;
}
}
if (!fl) continue;
}
var commentContents = st.ToString();
char[] delim = { ' ', '\n', '\t', '\r' };
var ar = commentContents.Split(delim, StringSplitOptions.RemoveEmptyEntries);
if (ar.Length != 2 || ar[0] != "add") continue;
var lineToAdd = TestEntryArgName + ".AddElement(" + ar[1] + ")";
var linelist = new List<ExpressionStatementSyntax>();
linelist.Add(SyntaxFactory.ExpressionStatement(SyntaxFactory.ParseExpression(lineToAdd)));
var childlist = s.Parent.ChildNodes();
foreach (var si in childlist)
{
if (s != si) continue;
if (before) newmethod = newmethod.InsertNodesBefore(si, linelist);
else newmethod = newmethod.InsertNodesAfter(si, linelist);
break;
}
break;
}
return newmethod;
}
I need to replace all such comments in my method. This function only inserts node, and does it only one time.
Edit. On this moment, I've got the following solution, but it seems too complicated and not obvious...
public static MethodDeclarationSyntax getChangedNode(MethodDeclarationSyntax method)
{
var TestEntryArgName = "__tst";
/* Adding last param */
var parlist = method.ChildNodes().OfType<ParameterListSyntax>().First();
var newparlist = parlist.AddParameters(SyntaxFactory.Parameter(
SyntaxFactory.Identifier(TestEntryArgName))
.WithType(SyntaxFactory.ParseTypeName("Heap ")));
var newmethod = method.ReplaceNode(parlist, newparlist);
/* Adding __tst.AddElement(i); */
while (true) {
IEnumerable<SyntaxNode> desc;
bool triviaFound;
desc = newmethod.Body.DescendantNodes();
triviaFound = false;
foreach (var s in desc)
{
SyntaxTrivia st = SyntaxFactory.SyntaxTrivia(SyntaxKind.WhitespaceTrivia, " ");
bool fl = false;
bool before = true;
var lt = s.GetLeadingTrivia();
foreach (var triviaEntry in lt)
{
if (triviaEntry.Kind() == SyntaxKind.SingleLineDocumentationCommentTrivia)
{
fl = true;
st = triviaEntry;
break;
}
}
if (!fl)
{
lt = s.GetTrailingTrivia();
before = false;
foreach (var triviaEntry in lt)
{
if (triviaEntry.Kind() == SyntaxKind.SingleLineDocumentationCommentTrivia)
{
fl = true;
st = triviaEntry;
break;
}
}
if (!fl) continue;
}
var commentContents = st.ToString();
char[] delim = { ' ', '\n', '\t', '\r' };
var ar = commentContents.Split(delim, StringSplitOptions.RemoveEmptyEntries);
if (ar.Length != 2 || ar[0] != "add") continue;
var lineToAdd = TestEntryArgName + ".AddElement(" + ar[1] + ")";
var linelist = new List<ExpressionStatementSyntax>();
linelist.Add(SyntaxFactory.ExpressionStatement(SyntaxFactory.ParseExpression(lineToAdd)));
var childlist = s.Parent.ChildNodes();
foreach (var si in childlist)
{
if (s != si) continue;
if (before) newmethod = newmethod.InsertNodesBefore(si, linelist);
else newmethod = newmethod.InsertNodesAfter(si, linelist);
break;
}
var newTrvias = newmethod.DescendantTrivia().Where((t) =>
{
if (t.Kind() != SyntaxKind.SingleLineDocumentationCommentTrivia)
return false;
var arr = t.ToString().Split(delim, StringSplitOptions.RemoveEmptyEntries);
return arr.Length == 2 && arr[0] == "add";
});
newmethod = newmethod.ReplaceTrivia(newTrvias.First(), SyntaxFactory.SyntaxTrivia(SyntaxKind.WhitespaceTrivia, " "));
triviaFound = true;
break;
}
if (!triviaFound) break;
}
return newmethod;
}
What You are looking for is probably CSharpSyntaxRewriter.This is a class in roslyn that visits each node, token and trivia in syntactic model of Your code. You can make your own rewriter, that overrides VisitTrivia and returns the expression You want to get.For example:
public class MyRewriter : CSharpSyntaxRewriter
{
public MyRewriter(): base(true)
{
}
public override SyntaxTrivia VisitTrivia(SyntaxTrivia trivia)
{
if(trivia.Kind() == SyntaxKind.SingleLineDocumentationCommentTrivia)
{
string xml = trivia.ToFullString();
var TestEntryArgName = "__tst";
char[] delim = { ' ', '\n', '\t', '\r' };
var ar = xml.Split(delim, StringSplitOptions.RemoveEmptyEntries);
if (ar.Length != 3 || ar[1] != "add")
{
return base.VisitTrivia(trivia);
}
var lineToAdd = TestEntryArgName + ".AddElement(" + ar[2] + ")";
var expression = SyntaxFactory.SyntaxTrivia(SyntaxKind.SingleLineCommentTrivia, lineToAdd);
return expression;
}
return base.VisitTrivia(trivia);
}
}
sample use:
var myRewriter = new MyRewriter();
string code = "";
using (StreamReader sr = new StreamReader("Program.cs"))
{
code = sr.ReadToEnd();
}
var tree = CSharpSyntaxTree.ParseText(code);
var node = tree.GetRoot();
using(StreamWriter sw = new StreamWriter("Program.cs"))
{
sw.Write(myRewriter.Visit(node));
}
I found a simple solution, and I think, it suits me, but, I know, it can not be called a "right" solution, because it deals with the source code. Anyway:
public static MethodDeclarationSyntax getChangedNode(MethodDeclarationSyntax newmethod)
{
/* Adding __tst.AddElement(i); */
while (true)
{
var desc = newmethod.Body.DescendantTrivia().Where((t) => t.IsKind(SyntaxKind.SingleLineDocumentationCommentTrivia));
var triviaFound = false;
foreach (var s in desc)
{
var commentContents = s.ToString();
char[] delim = { ' ', '\n', '\t', '\r' };
var ar = commentContents.Split(delim, StringSplitOptions.RemoveEmptyEntries);
if (ar.Length != 2 || ar[0] != "add") continue;
var lineToAdd = "\r\n__tst.AddElement(" + ar[1] + ");\r\n";
newmethod = CSharpSyntaxTree.ParseText(newmethod.GetText().Replace(s.FullSpan, lineToAdd))
.GetRoot().DescendantNodes().OfType<MethodDeclarationSyntax>().First();
triviaFound = true;
break;
}
if (!triviaFound) break;
}
return newmethod;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With