I have been trying to use libSyntax for some basic refactoring and have a couple of questions:
a) Is there some documentation available for the various APIs in SyntaxFactory and the visitor functions? The libSyntax github link has a useful example but the locations of the docs was not very obvious.
b) I am trying to create an object of type StmtSyntax from the node.body of IfStmt.
But, the bridge between the node.body's CodeBlockItemListSyntax (given by node.body.statements) and the StmtSyntax object is unclear. The code below uses makeIfStmt and achieves the purpose partially:
We don't have extensive documentation for syntax factory yet. Most of our existing doc-comments are for SyntaxNodes. If you have specific questions about syntax factory or visitor functions, feel free to ask here in the forum.
First of all, the //then part of the code is attached to the closing brace } since there're no other statement after //then. To extract the dangling comments like this, we have to visit the end brace } instead of collecting node.body. One principle of libSyntax's structure is that comments must attach to an existing token syntax.
To walk-around the bridging problem, the following code example works for me:
Thanks @Xi_Ge. It works by removing the braces but does not remove the blank spaces (the trivia?) attached to the node, thereby leaving the existing indentation, due to the if condition, intact. I want the indentation of the entire then body to be removed as well. I couldn't find APIs (similiar to withoutLeadingTrivia...) on CodeBlockSyntax (node.body) that removes the trivia. Do you have suggestions for fixing that?
PS: I can potentially run swiftformat on the modified code (which does the job, btw!) but I want to understand these APIs in more detail.
Then the think you want to visit is CodeBlockItemListSyntax, and iterate over all elements in the code block. You'll need to do this because you can't replace a single if statement with potentially multiple statements inside the if statement -- you have to add the inner statements to the code block which the if statement lives inside.
I am now able to attach multiple statements within the then branch of an IfStmt using the following:
override func visit(_ node: CodeBlockItemListSyntax) -> Syntax {
var newBody = SyntaxFactory.makeBlankCodeBlockItemList()
for stmt in node {
if let ifNode = stmt.item as? IfStmtSyntax {
for s in ifNode.body.statements {
newBody = newBody.appending(s)
}
} else {
newBody = newBody.appending(stmt)
}
}
return super.visit(newBody)
}
I had to use appending because CodeBlockListItemSyntax doesn't have a withBody unlike ForInStmtSyntax. But, the problem with removing the additional indentation due to if is still not fixed. It appears that the code transformation for using where clause that you pointed out also suffers from the same issue (copied the relevant parts and tried it out on a simple example, it removes the if statement but it leaves double indentation -- one for the for and the other for the if).
If there is a pointer to remove the addl. indentation, please let me know.
PS: When I copied your code, there was a compiler error due to the absence of lastToken in ExprSyntax. This is in let lastToken = node.sequenceExpr.lastToken
Unfortunately there’s no good way (right now) to remove the extra indentation — you’ll need to roll your own solution for that.
Thanks for catching my mistake in the code sample! You might have some more luck using a var newBody = [CodeBlockItem]() instead of appending to a CodeBlockItemList, and constructing one at the end using SyntaxFactory.makeCodeBlockItemList(newBody)