2025-10-15 10:12:44 +03:00

131 lines
3.5 KiB
Go

package lsutil
import (
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/ast"
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/astnav"
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/core"
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/scanner"
)
// Replaces last(node.getChildren(sourceFile))
func GetLastChild(node *ast.Node, sourceFile *ast.SourceFile) *ast.Node {
lastChildNode := GetLastVisitedChild(node, sourceFile)
if ast.IsJSDocSingleCommentNode(node) && lastChildNode == nil {
return nil
}
var tokenStartPos int
if lastChildNode != nil {
tokenStartPos = lastChildNode.End()
} else {
tokenStartPos = node.Pos()
}
var lastToken *ast.Node
scanner := scanner.GetScannerForSourceFile(sourceFile, tokenStartPos)
for startPos := tokenStartPos; startPos < node.End(); {
tokenKind := scanner.Token()
tokenFullStart := scanner.TokenFullStart()
tokenEnd := scanner.TokenEnd()
lastToken = sourceFile.GetOrCreateToken(tokenKind, tokenFullStart, tokenEnd, node)
startPos = tokenEnd
scanner.Scan()
}
return core.IfElse(lastToken != nil, lastToken, lastChildNode)
}
func GetLastToken(node *ast.Node, sourceFile *ast.SourceFile) *ast.Node {
if node == nil {
return nil
}
if ast.IsTokenKind(node.Kind) || ast.IsIdentifier(node) {
return nil
}
AssertHasRealPosition(node)
lastChild := GetLastChild(node, sourceFile)
if lastChild == nil {
return nil
}
if lastChild.Kind < ast.KindFirstNode {
return lastChild
} else {
return GetLastToken(lastChild, sourceFile)
}
}
// Gets the last visited child of the given node.
// NOTE: This doesn't include unvisited tokens; for this, use `getLastChild` or `getLastToken`.
func GetLastVisitedChild(node *ast.Node, sourceFile *ast.SourceFile) *ast.Node {
var lastChild *ast.Node
visitNode := func(n *ast.Node, _ *ast.NodeVisitor) *ast.Node {
if !(n == nil || node.Flags&ast.NodeFlagsReparsed != 0) {
lastChild = n
}
return n
}
visitNodeList := func(nodeList *ast.NodeList, _ *ast.NodeVisitor) *ast.NodeList {
if nodeList != nil && len(nodeList.Nodes) > 0 {
for i := len(nodeList.Nodes) - 1; i >= 0; i-- {
if nodeList.Nodes[i].Flags&ast.NodeFlagsReparsed == 0 {
lastChild = nodeList.Nodes[i]
break
}
}
}
return nodeList
}
astnav.VisitEachChildAndJSDoc(node, sourceFile, visitNode, visitNodeList)
return lastChild
}
func GetFirstToken(node *ast.Node, sourceFile *ast.SourceFile) *ast.Node {
if ast.IsIdentifier(node) || ast.IsTokenKind(node.Kind) {
return nil
}
AssertHasRealPosition(node)
var firstChild *ast.Node
node.ForEachChild(func(n *ast.Node) bool {
if n == nil || node.Flags&ast.NodeFlagsReparsed != 0 {
return false
}
firstChild = n
return true
})
var tokenEndPosition int
if firstChild != nil {
tokenEndPosition = firstChild.Pos()
} else {
tokenEndPosition = node.End()
}
scanner := scanner.GetScannerForSourceFile(sourceFile, node.Pos())
var firstToken *ast.Node
if node.Pos() < tokenEndPosition {
tokenKind := scanner.Token()
tokenFullStart := scanner.TokenFullStart()
tokenEnd := scanner.TokenEnd()
firstToken = sourceFile.GetOrCreateToken(tokenKind, tokenFullStart, tokenEnd, node)
}
if firstToken != nil {
return firstToken
}
if firstChild == nil {
return nil
}
if firstChild.Kind < ast.KindFirstNode {
return firstChild
}
return GetFirstToken(firstChild, sourceFile)
}
func AssertHasRealPosition(node *ast.Node) {
if ast.PositionIsSynthesized(node.Pos()) || ast.PositionIsSynthesized(node.End()) {
panic("Node must have a real position for this operation.")
}
}