func absoluteOffsetsForNode(fset *token.FileSet, n ast.Node) (start, end int) {
	return fset.PositionFor(n.Pos(), false).Offset, fset.PositionFor(n.End(), false).Offset
}
absoluteOffsetsForNode derives the absolute character offsets of the node start and
end positions.
package outline
import (
	"go/ast"
	"go/token"
	"strconv"
)
const (
	undefinedTextAlt = "undefined"
)
type ginkgoMetadata struct {
	Name string `json:"name"`
	Text string `json:"text"`
	Start int `json:"start"`
	End int `json:"end"`
	Spec    bool `json:"spec"`
	Focused bool `json:"focused"`
	Pending bool `json:"pending"`
}
type ginkgoNode struct {
	ginkgoMetadata
	Nodes []*ginkgoNode `json:"nodes"`
}
type walkFunc func(n *ginkgoNode)
func (n *ginkgoNode) PreOrder(f walkFunc) {
	f(n)
	for _, m := range n.Nodes {
		m.PreOrder(f)
	}
}
func (n *ginkgoNode) PostOrder(f walkFunc) {
	for _, m := range n.Nodes {
		m.PostOrder(f)
	}
	f(n)
}
func (n *ginkgoNode) Walk(pre, post walkFunc) {
	pre(n)
	for _, m := range n.Nodes {
		m.Walk(pre, post)
	}
	post(n)
}
func (n *ginkgoNode) PropagateInheritedProperties() {
	n.PreOrder(func(thisNode *ginkgoNode) {
		for _, descendantNode := range thisNode.Nodes {
			if thisNode.Pending {
				descendantNode.Pending = true
				descendantNode.Focused = false
			}
			if thisNode.Focused && !descendantNode.Pending {
				descendantNode.Focused = true
			}
		}
	})
}
func (n *ginkgoNode) BackpropagateUnfocus() {
	focusedSpecInSubtreeStack := []bool{}
	n.PostOrder(func(thisNode *ginkgoNode) {
		if thisNode.Spec {
			focusedSpecInSubtreeStack = append(focusedSpecInSubtreeStack, thisNode.Focused)
			return
		}
		focusedSpecInSubtree := false
		for range thisNode.Nodes {
			focusedSpecInSubtree = focusedSpecInSubtree || focusedSpecInSubtreeStack[len(focusedSpecInSubtreeStack)-1]
			focusedSpecInSubtreeStack = focusedSpecInSubtreeStack[0 : len(focusedSpecInSubtreeStack)-1]
		}
		focusedSpecInSubtreeStack = append(focusedSpecInSubtreeStack, focusedSpecInSubtree)
		if focusedSpecInSubtree {
			thisNode.Focused = false
		}
	})
}
func packageAndIdentNamesFromCallExpr(ce *ast.CallExpr) (string, string, bool) {
	switch ex := ce.Fun.(type) {
	case *ast.Ident:
		return "", ex.Name, true
	case *ast.SelectorExpr:
		pkgID, ok := ex.X.(*ast.Ident)
		if !ok {
			return "", "", false
		}
		if pkgID.Obj != nil {
			return "", "", false
		}
		if ex.Sel == nil {
			return "", "", false
		}
		return pkgID.Name, ex.Sel.Name, true
	default:
		return "", "", false
	}
}