kittenipc/kitcom/internal/tsgo/printer/changetrackerwriter.go
2025-10-15 10:12:44 +03:00

232 lines
6.9 KiB
Go

package printer
import (
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/ast"
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/core"
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/scanner"
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/stringutil"
)
type ChangeTrackerWriter struct {
textWriter
lastNonTriviaPosition int
pos map[triviaPositionKey]int
end map[triviaPositionKey]int
}
type triviaPositionKey interface { // *astNode | *ast.NodeList
Pos() int
End() int
}
func NewChangeTrackerWriter(newline string) *ChangeTrackerWriter {
ctw := &ChangeTrackerWriter{
textWriter: textWriter{newLine: newline},
lastNonTriviaPosition: 0,
pos: map[triviaPositionKey]int{},
end: map[triviaPositionKey]int{},
}
ctw.textWriter.Clear()
return ctw
}
func (ct *ChangeTrackerWriter) GetPrintHandlers() PrintHandlers {
return PrintHandlers{
OnBeforeEmitNode: func(nodeOpt *ast.Node) {
if nodeOpt != nil {
ct.setPos(nodeOpt)
}
},
OnAfterEmitNode: func(nodeOpt *ast.Node) {
if nodeOpt != nil {
ct.setEnd(nodeOpt)
}
},
OnBeforeEmitNodeList: func(nodesOpt *ast.NodeList) {
if nodesOpt != nil {
ct.setPos(nodesOpt)
}
},
OnAfterEmitNodeList: func(nodesOpt *ast.NodeList) {
if nodesOpt != nil {
ct.setEnd(nodesOpt)
}
},
OnBeforeEmitToken: func(nodeOpt *ast.TokenNode) {
if nodeOpt != nil {
ct.setPos(nodeOpt)
}
},
OnAfterEmitToken: func(nodeOpt *ast.TokenNode) {
if nodeOpt != nil {
ct.setEnd(nodeOpt)
}
},
}
}
func (ct *ChangeTrackerWriter) setPos(node triviaPositionKey) {
ct.pos[node] = ct.lastNonTriviaPosition
}
func (ct *ChangeTrackerWriter) setEnd(node triviaPositionKey) {
ct.end[node] = ct.lastNonTriviaPosition
}
func (ct *ChangeTrackerWriter) getPos(node triviaPositionKey) int {
return ct.pos[node]
}
func (ct *ChangeTrackerWriter) getEnd(node triviaPositionKey) int {
return ct.end[node]
}
func (ct *ChangeTrackerWriter) setLastNonTriviaPosition(s string, force bool) {
if force || scanner.SkipTrivia(s, 0) != len(s) {
ct.lastNonTriviaPosition = ct.textWriter.GetTextPos()
i := 0
for stringutil.IsWhiteSpaceLike(rune(s[len(s)-i-1])) {
i++
}
// trim trailing whitespaces
ct.lastNonTriviaPosition -= i
}
}
func (ct *ChangeTrackerWriter) AssignPositionsToNode(node *ast.Node, factory *ast.NodeFactory) *ast.Node {
var visitor *ast.NodeVisitor
visitor = &ast.NodeVisitor{
Visit: func(n *ast.Node) *ast.Node { return ct.assignPositionsToNodeWorker(n, visitor) },
Factory: factory,
Hooks: ast.NodeVisitorHooks{
VisitNode: ct.assignPositionsToNodeWorker,
VisitNodes: ct.assignPositionsToNodeArray,
VisitToken: ct.assignPositionsToNodeWorker,
VisitModifiers: func(modifiers *ast.ModifierList, v *ast.NodeVisitor) *ast.ModifierList {
if modifiers != nil {
ct.assignPositionsToNodeArray(&modifiers.NodeList, v)
}
return modifiers
},
},
}
return ct.assignPositionsToNodeWorker(node, visitor)
}
func (ct *ChangeTrackerWriter) assignPositionsToNodeWorker(
node *ast.Node,
v *ast.NodeVisitor,
) *ast.Node {
if node == nil {
return node
}
visited := node.VisitEachChild(v)
// create proxy node for non synthesized nodes
newNode := visited
if !ast.NodeIsSynthesized(visited) {
newNode = visited.Clone(v.Factory)
}
newNode.ForEachChild(func(child *ast.Node) bool {
child.Parent = newNode
return true
})
newNode.Loc = core.NewTextRange(ct.getPos(node), ct.getEnd(node))
return newNode
}
func (ct *ChangeTrackerWriter) assignPositionsToNodeArray(
nodes *ast.NodeList,
v *ast.NodeVisitor,
) *ast.NodeList {
visited := v.VisitNodes(nodes)
if visited == nil {
return visited
}
if nodes == nil {
// Debug.assert(nodes);
panic("if nodes is nil, visited should not be nil")
}
// clone nodearray if necessary
nodeArray := visited
if visited == nodes {
nodeArray = visited.Clone(v.Factory)
}
nodeArray.Loc = core.NewTextRange(ct.getPos(nodes), ct.getEnd(nodes))
return nodeArray
}
func (ct *ChangeTrackerWriter) Write(text string) {
ct.textWriter.Write(text)
ct.setLastNonTriviaPosition(text, false)
}
func (ct *ChangeTrackerWriter) WriteTrailingSemicolon(text string) {
ct.textWriter.WriteTrailingSemicolon(text)
ct.setLastNonTriviaPosition(text, false)
}
func (ct *ChangeTrackerWriter) WriteComment(text string) { ct.textWriter.WriteComment(text) }
func (ct *ChangeTrackerWriter) WriteKeyword(text string) {
ct.textWriter.WriteKeyword(text)
ct.setLastNonTriviaPosition(text, false)
}
func (ct *ChangeTrackerWriter) WriteOperator(text string) {
ct.textWriter.WriteOperator(text)
ct.setLastNonTriviaPosition(text, false)
}
func (ct *ChangeTrackerWriter) WritePunctuation(text string) {
ct.textWriter.WritePunctuation(text)
ct.setLastNonTriviaPosition(text, false)
}
func (ct *ChangeTrackerWriter) WriteSpace(text string) {
ct.textWriter.WriteSpace(text)
ct.setLastNonTriviaPosition(text, false)
}
func (ct *ChangeTrackerWriter) WriteStringLiteral(text string) {
ct.textWriter.WriteStringLiteral(text)
ct.setLastNonTriviaPosition(text, false)
}
func (ct *ChangeTrackerWriter) WriteParameter(text string) {
ct.textWriter.WriteParameter(text)
ct.setLastNonTriviaPosition(text, false)
}
func (ct *ChangeTrackerWriter) WriteProperty(text string) {
ct.textWriter.WriteProperty(text)
ct.setLastNonTriviaPosition(text, false)
}
func (ct *ChangeTrackerWriter) WriteSymbol(text string, symbol *ast.Symbol) {
ct.textWriter.WriteSymbol(text, symbol)
ct.setLastNonTriviaPosition(text, false)
}
func (ct *ChangeTrackerWriter) WriteLine() { ct.textWriter.WriteLine() }
func (ct *ChangeTrackerWriter) WriteLineForce(force bool) { ct.textWriter.WriteLineForce(force) }
func (ct *ChangeTrackerWriter) IncreaseIndent() { ct.textWriter.IncreaseIndent() }
func (ct *ChangeTrackerWriter) DecreaseIndent() { ct.textWriter.DecreaseIndent() }
func (ct *ChangeTrackerWriter) Clear() { ct.textWriter.Clear(); ct.lastNonTriviaPosition = 0 }
func (ct *ChangeTrackerWriter) String() string { return ct.textWriter.String() }
func (ct *ChangeTrackerWriter) RawWrite(s string) {
ct.textWriter.RawWrite(s)
ct.setLastNonTriviaPosition(s, false)
}
func (ct *ChangeTrackerWriter) WriteLiteral(s string) {
ct.textWriter.WriteLiteral(s)
ct.setLastNonTriviaPosition(s, true)
}
func (ct *ChangeTrackerWriter) GetTextPos() int { return ct.textWriter.GetTextPos() }
func (ct *ChangeTrackerWriter) GetLine() int { return ct.textWriter.GetLine() }
func (ct *ChangeTrackerWriter) GetColumn() int { return ct.textWriter.GetColumn() }
func (ct *ChangeTrackerWriter) GetIndent() int { return ct.textWriter.GetIndent() }
func (ct *ChangeTrackerWriter) IsAtStartOfLine() bool { return ct.textWriter.IsAtStartOfLine() }
func (ct *ChangeTrackerWriter) HasTrailingComment() bool { return ct.textWriter.HasTrailingComment() }
func (ct *ChangeTrackerWriter) HasTrailingWhitespace() bool {
return ct.textWriter.HasTrailingWhitespace()
}