func foldingRangeFunc(fset *token.FileSet, m *protocol.ColumnMapper, n ast.Node, lineFoldingOnly bool) *FoldingRangeInfo {
	var kind protocol.FoldingRangeKind
	var start, end token.Pos
	switch n := n.(type) {
	case *ast.BlockStmt:
		var startList, endList token.Pos
		if num := len(n.List); num != 0 {
			startList, endList = n.List[0].Pos(), n.List[num-1].End()
		}
		start, end = validLineFoldingRange(fset, n.Lbrace, n.Rbrace, startList, endList, lineFoldingOnly)
	case *ast.CaseClause:
		start, end = n.Colon+1, n.End()
	case *ast.CommClause:
		start, end = n.Colon+1, n.End()
	case *ast.CallExpr:
		start, end = n.Lparen+1, n.Rparen
	case *ast.FieldList:
		var startList, endList token.Pos
		if num := len(n.List); num != 0 {
			startList, endList = n.List[0].Pos(), n.List[num-1].End()
		}
		start, end = validLineFoldingRange(fset, n.Opening, n.Closing, startList, endList, lineFoldingOnly)
	case *ast.GenDecl:
		if n.Tok == token.IMPORT {
			kind = protocol.Imports
		}
		var startSpecs, endSpecs token.Pos
		if num := len(n.Specs); num != 0 {
			startSpecs, endSpecs = n.Specs[0].Pos(), n.Specs[num-1].End()
		}
		start, end = validLineFoldingRange(fset, n.Lparen, n.Rparen, startSpecs, endSpecs, lineFoldingOnly)
	case *ast.CompositeLit:
		var startElts, endElts token.Pos
		if num := len(n.Elts); num != 0 {
			startElts, endElts = n.Elts[0].Pos(), n.Elts[num-1].End()
		}
		start, end = validLineFoldingRange(fset, n.Lbrace, n.Rbrace, startElts, endElts, lineFoldingOnly)
	}
	if !start.IsValid() || !end.IsValid() {
		return nil
	}
	if lineFoldingOnly && fset.Position(start).Line == fset.Position(end).Line {
		return nil
	}
	return &FoldingRangeInfo{
		MappedRange: NewMappedRange(fset, m, start, end),
		Kind:        kind,
	}
}
foldingRangeFunc calculates the line folding range for ast.Node n
package source
import (
	"context"
	"go/ast"
	"go/token"
	"sort"
	"strings"
	"github.com/govim/govim/cmd/govim/internal/golang_org_x_tools/lsp/protocol"
)
type FoldingRangeInfo struct {
	MappedRange
	Kind protocol.FoldingRangeKind
}
func FoldingRange(ctx context.Context, snapshot Snapshot, fh FileHandle, lineFoldingOnly bool) (ranges []*FoldingRangeInfo, err error) {
	pgf, err := snapshot.ParseGo(ctx, fh, ParseFull)
	if err != nil {
		return nil, err
	}
	if pgf.ParseErr != nil {
		return nil, nil
	}
	fset := snapshot.FileSet()
	ranges = append(ranges, commentsFoldingRange(fset, pgf.Mapper, pgf.File)...)
	visit := func(n ast.Node) bool {
		rng := foldingRangeFunc(fset, pgf.Mapper, n, lineFoldingOnly)
		if rng != nil {
			ranges = append(ranges, rng)
		}
		return true
	}
	ast.Inspect(pgf.File, visit)
	sort.Slice(ranges, func(i, j int) bool {
		irng, _ := ranges[i].Range()
		jrng, _ := ranges[j].Range()
		return protocol.CompareRange(irng, jrng) < 0
	})
	return ranges, nil
}