mirror of
https://github.com/jesseduffield/lazygit.git
synced 2025-08-04 23:42:10 +03:00
fix reflog failing to properly refresh
This commit is contained in:
528
vendor/github.com/sanity-io/litter/dump.go
generated
vendored
Normal file
528
vendor/github.com/sanity-io/litter/dump.go
generated
vendored
Normal file
@@ -0,0 +1,528 @@
|
||||
package litter
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var (
|
||||
packageNameStripperRegexp = regexp.MustCompile(`\b[a-zA-Z_]+[a-zA-Z_0-9]+\.`)
|
||||
compactTypeRegexp = regexp.MustCompile(`\s*([,;{}()])\s*`)
|
||||
)
|
||||
|
||||
// Dumper is the interface for implementing custom dumper for your types.
|
||||
type Dumper interface {
|
||||
LitterDump(w io.Writer)
|
||||
}
|
||||
|
||||
// Options represents configuration options for litter
|
||||
type Options struct {
|
||||
Compact bool
|
||||
StripPackageNames bool
|
||||
HidePrivateFields bool
|
||||
HideZeroValues bool
|
||||
FieldExclusions *regexp.Regexp
|
||||
FieldFilter func(reflect.StructField, reflect.Value) bool
|
||||
HomePackage string
|
||||
Separator string
|
||||
StrictGo bool
|
||||
DumpFunc func(reflect.Value, io.Writer) bool
|
||||
|
||||
// DisablePointerReplacement, if true, disables the replacing of pointer data with variable names
|
||||
// when it's safe. This is useful for diffing two structures, where pointer variables would cause
|
||||
// false changes. However, circular graphs are still detected and elided to avoid infinite output.
|
||||
DisablePointerReplacement bool
|
||||
}
|
||||
|
||||
// Config is the default config used when calling Dump
|
||||
var Config = Options{
|
||||
StripPackageNames: false,
|
||||
HidePrivateFields: true,
|
||||
FieldExclusions: regexp.MustCompile(`^(XXX_.*)$`), // XXX_ is a prefix of fields generated by protoc-gen-go
|
||||
Separator: " ",
|
||||
}
|
||||
|
||||
type dumpState struct {
|
||||
w io.Writer
|
||||
depth int
|
||||
config *Options
|
||||
pointers ptrmap
|
||||
visitedPointers ptrmap
|
||||
parentPointers ptrmap
|
||||
currentPointer *ptrinfo
|
||||
homePackageRegexp *regexp.Regexp
|
||||
}
|
||||
|
||||
func (s *dumpState) write(b []byte) {
|
||||
if _, err := s.w.Write(b); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *dumpState) writeString(str string) {
|
||||
s.write([]byte(str))
|
||||
}
|
||||
|
||||
func (s *dumpState) indent() {
|
||||
if !s.config.Compact {
|
||||
s.write(bytes.Repeat([]byte(" "), s.depth))
|
||||
}
|
||||
}
|
||||
|
||||
func (s *dumpState) newlineWithPointerNameComment() {
|
||||
if ptr := s.currentPointer; ptr != nil {
|
||||
if s.config.Compact {
|
||||
s.write([]byte(fmt.Sprintf("/*%s*/", ptr.label())))
|
||||
} else {
|
||||
s.write([]byte(fmt.Sprintf(" // %s\n", ptr.label())))
|
||||
}
|
||||
s.currentPointer = nil
|
||||
return
|
||||
}
|
||||
if !s.config.Compact {
|
||||
s.write([]byte("\n"))
|
||||
}
|
||||
}
|
||||
|
||||
func (s *dumpState) dumpType(v reflect.Value) {
|
||||
typeName := v.Type().String()
|
||||
if s.config.StripPackageNames {
|
||||
typeName = packageNameStripperRegexp.ReplaceAllLiteralString(typeName, "")
|
||||
} else if s.homePackageRegexp != nil {
|
||||
typeName = s.homePackageRegexp.ReplaceAllLiteralString(typeName, "")
|
||||
}
|
||||
if s.config.Compact {
|
||||
typeName = compactTypeRegexp.ReplaceAllString(typeName, "$1")
|
||||
}
|
||||
s.write([]byte(typeName))
|
||||
}
|
||||
|
||||
func (s *dumpState) dumpSlice(v reflect.Value) {
|
||||
s.dumpType(v)
|
||||
numEntries := v.Len()
|
||||
if numEntries == 0 {
|
||||
s.write([]byte("{}"))
|
||||
return
|
||||
}
|
||||
s.write([]byte("{"))
|
||||
s.newlineWithPointerNameComment()
|
||||
s.depth++
|
||||
for i := 0; i < numEntries; i++ {
|
||||
s.indent()
|
||||
s.dumpVal(v.Index(i))
|
||||
if !s.config.Compact || i < numEntries-1 {
|
||||
s.write([]byte(","))
|
||||
}
|
||||
s.newlineWithPointerNameComment()
|
||||
}
|
||||
s.depth--
|
||||
s.indent()
|
||||
s.write([]byte("}"))
|
||||
}
|
||||
|
||||
func (s *dumpState) dumpStruct(v reflect.Value) {
|
||||
dumpPreamble := func() {
|
||||
s.dumpType(v)
|
||||
s.write([]byte("{"))
|
||||
s.newlineWithPointerNameComment()
|
||||
s.depth++
|
||||
}
|
||||
preambleDumped := false
|
||||
vt := v.Type()
|
||||
numFields := v.NumField()
|
||||
for i := 0; i < numFields; i++ {
|
||||
vtf := vt.Field(i)
|
||||
if s.config.HidePrivateFields && vtf.PkgPath != "" || s.config.FieldExclusions != nil && s.config.FieldExclusions.MatchString(vtf.Name) {
|
||||
continue
|
||||
}
|
||||
if s.config.FieldFilter != nil && !s.config.FieldFilter(vtf, v.Field(i)) {
|
||||
continue
|
||||
}
|
||||
if s.config.HideZeroValues && isZeroValue(v.Field(i)) {
|
||||
continue
|
||||
}
|
||||
if !preambleDumped {
|
||||
dumpPreamble()
|
||||
preambleDumped = true
|
||||
}
|
||||
s.indent()
|
||||
s.write([]byte(vtf.Name))
|
||||
if s.config.Compact {
|
||||
s.write([]byte(":"))
|
||||
} else {
|
||||
s.write([]byte(": "))
|
||||
}
|
||||
s.dumpVal(v.Field(i))
|
||||
if !s.config.Compact || i < numFields-1 {
|
||||
s.write([]byte(","))
|
||||
}
|
||||
s.newlineWithPointerNameComment()
|
||||
}
|
||||
if preambleDumped {
|
||||
s.depth--
|
||||
s.indent()
|
||||
s.write([]byte("}"))
|
||||
} else {
|
||||
// There were no fields dumped
|
||||
s.dumpType(v)
|
||||
s.write([]byte("{}"))
|
||||
}
|
||||
}
|
||||
|
||||
func (s *dumpState) dumpMap(v reflect.Value) {
|
||||
if v.IsNil() {
|
||||
s.dumpType(v)
|
||||
s.writeString("(nil)")
|
||||
return
|
||||
}
|
||||
|
||||
s.dumpType(v)
|
||||
|
||||
keys := v.MapKeys()
|
||||
if len(keys) == 0 {
|
||||
s.write([]byte("{}"))
|
||||
return
|
||||
}
|
||||
|
||||
s.write([]byte("{"))
|
||||
s.newlineWithPointerNameComment()
|
||||
s.depth++
|
||||
sort.Sort(mapKeySorter{
|
||||
keys: keys,
|
||||
options: s.config,
|
||||
})
|
||||
numKeys := len(keys)
|
||||
for i, key := range keys {
|
||||
s.indent()
|
||||
s.dumpVal(key)
|
||||
if s.config.Compact {
|
||||
s.write([]byte(":"))
|
||||
} else {
|
||||
s.write([]byte(": "))
|
||||
}
|
||||
s.dumpVal(v.MapIndex(key))
|
||||
if !s.config.Compact || i < numKeys-1 {
|
||||
s.write([]byte(","))
|
||||
}
|
||||
s.newlineWithPointerNameComment()
|
||||
}
|
||||
s.depth--
|
||||
s.indent()
|
||||
s.write([]byte("}"))
|
||||
}
|
||||
|
||||
func (s *dumpState) dumpFunc(v reflect.Value) {
|
||||
parts := strings.Split(runtime.FuncForPC(v.Pointer()).Name(), "/")
|
||||
name := parts[len(parts)-1]
|
||||
|
||||
// Anonymous function
|
||||
if strings.Count(name, ".") > 1 {
|
||||
s.dumpType(v)
|
||||
} else {
|
||||
if s.config.StripPackageNames {
|
||||
name = packageNameStripperRegexp.ReplaceAllLiteralString(name, "")
|
||||
} else if s.homePackageRegexp != nil {
|
||||
name = s.homePackageRegexp.ReplaceAllLiteralString(name, "")
|
||||
}
|
||||
if s.config.Compact {
|
||||
name = compactTypeRegexp.ReplaceAllString(name, "$1")
|
||||
}
|
||||
s.write([]byte(name))
|
||||
}
|
||||
}
|
||||
|
||||
func (s *dumpState) dumpChan(v reflect.Value) {
|
||||
vType := v.Type()
|
||||
res := []byte(vType.String())
|
||||
s.write(res)
|
||||
}
|
||||
|
||||
func (s *dumpState) dumpCustom(v reflect.Value, buf *bytes.Buffer) {
|
||||
|
||||
// Dump the type
|
||||
s.dumpType(v)
|
||||
|
||||
if s.config.Compact {
|
||||
s.write(buf.Bytes())
|
||||
return
|
||||
}
|
||||
|
||||
// Now output the dump taking care to apply the current indentation-level
|
||||
// and pointer name comments.
|
||||
var err error
|
||||
firstLine := true
|
||||
for err == nil {
|
||||
var lineBytes []byte
|
||||
lineBytes, err = buf.ReadBytes('\n')
|
||||
line := strings.TrimRight(string(lineBytes), " \n")
|
||||
|
||||
if err != nil && err != io.EOF {
|
||||
break
|
||||
}
|
||||
// Do not indent first line
|
||||
if firstLine {
|
||||
firstLine = false
|
||||
} else {
|
||||
s.indent()
|
||||
}
|
||||
s.write([]byte(line))
|
||||
|
||||
// At EOF we're done
|
||||
if err == io.EOF {
|
||||
return
|
||||
}
|
||||
s.newlineWithPointerNameComment()
|
||||
}
|
||||
panic(err)
|
||||
}
|
||||
|
||||
func (s *dumpState) dump(value interface{}) {
|
||||
if value == nil {
|
||||
printNil(s.w)
|
||||
return
|
||||
}
|
||||
v := reflect.ValueOf(value)
|
||||
s.dumpVal(v)
|
||||
}
|
||||
|
||||
func (s *dumpState) descendIntoPossiblePointer(value reflect.Value, f func()) {
|
||||
canonicalize := true
|
||||
if isPointerValue(value) {
|
||||
// If elision disabled, and this is not a circular reference, don't canonicalize
|
||||
if s.config.DisablePointerReplacement && s.parentPointers.add(value) {
|
||||
canonicalize = false
|
||||
}
|
||||
|
||||
// Add to stack of pointers we're recursively descending into
|
||||
s.parentPointers.add(value)
|
||||
defer s.parentPointers.remove(value)
|
||||
}
|
||||
|
||||
if !canonicalize {
|
||||
ptr, _ := s.pointerFor(value)
|
||||
s.currentPointer = ptr
|
||||
f()
|
||||
return
|
||||
}
|
||||
|
||||
ptr, firstVisit := s.pointerFor(value)
|
||||
if ptr == nil {
|
||||
f()
|
||||
return
|
||||
}
|
||||
if firstVisit {
|
||||
s.currentPointer = ptr
|
||||
f()
|
||||
return
|
||||
}
|
||||
s.write([]byte(ptr.label()))
|
||||
}
|
||||
|
||||
func (s *dumpState) dumpVal(value reflect.Value) {
|
||||
if value.Kind() == reflect.Ptr && value.IsNil() {
|
||||
s.write([]byte("nil"))
|
||||
return
|
||||
}
|
||||
|
||||
v := deInterface(value)
|
||||
kind := v.Kind()
|
||||
|
||||
// Try to handle with dump func
|
||||
if s.config.DumpFunc != nil {
|
||||
buf := new(bytes.Buffer)
|
||||
if s.config.DumpFunc(v, buf) {
|
||||
s.dumpCustom(v, buf)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Handle custom dumpers
|
||||
dumperType := reflect.TypeOf((*Dumper)(nil)).Elem()
|
||||
if v.Type().Implements(dumperType) {
|
||||
s.descendIntoPossiblePointer(v, func() {
|
||||
// Run the custom dumper buffering the output
|
||||
buf := new(bytes.Buffer)
|
||||
dumpFunc := v.MethodByName("LitterDump")
|
||||
dumpFunc.Call([]reflect.Value{reflect.ValueOf(buf)})
|
||||
s.dumpCustom(v, buf)
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
switch kind {
|
||||
case reflect.Invalid:
|
||||
// Do nothing. We should never get here since invalid has already
|
||||
// been handled above.
|
||||
s.write([]byte("<invalid>"))
|
||||
|
||||
case reflect.Bool:
|
||||
printBool(s.w, v.Bool())
|
||||
|
||||
case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int:
|
||||
printInt(s.w, v.Int(), 10)
|
||||
|
||||
case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint:
|
||||
printUint(s.w, v.Uint(), 10)
|
||||
|
||||
case reflect.Float32:
|
||||
printFloat(s.w, v.Float(), 32)
|
||||
|
||||
case reflect.Float64:
|
||||
printFloat(s.w, v.Float(), 64)
|
||||
|
||||
case reflect.Complex64:
|
||||
printComplex(s.w, v.Complex(), 32)
|
||||
|
||||
case reflect.Complex128:
|
||||
printComplex(s.w, v.Complex(), 64)
|
||||
|
||||
case reflect.String:
|
||||
s.write([]byte(strconv.Quote(v.String())))
|
||||
|
||||
case reflect.Slice:
|
||||
if v.IsNil() {
|
||||
printNil(s.w)
|
||||
break
|
||||
}
|
||||
fallthrough
|
||||
|
||||
case reflect.Array:
|
||||
s.descendIntoPossiblePointer(v, func() {
|
||||
s.dumpSlice(v)
|
||||
})
|
||||
|
||||
case reflect.Interface:
|
||||
// The only time we should get here is for nil interfaces due to
|
||||
// unpackValue calls.
|
||||
if v.IsNil() {
|
||||
printNil(s.w)
|
||||
}
|
||||
|
||||
case reflect.Ptr:
|
||||
s.descendIntoPossiblePointer(v, func() {
|
||||
if s.config.StrictGo {
|
||||
s.writeString(fmt.Sprintf("(func(v %s) *%s { return &v })(", v.Elem().Type(), v.Elem().Type()))
|
||||
s.dumpVal(v.Elem())
|
||||
s.writeString(")")
|
||||
} else {
|
||||
s.writeString("&")
|
||||
s.dumpVal(v.Elem())
|
||||
}
|
||||
})
|
||||
|
||||
case reflect.Map:
|
||||
s.descendIntoPossiblePointer(v, func() {
|
||||
s.dumpMap(v)
|
||||
})
|
||||
|
||||
case reflect.Struct:
|
||||
s.dumpStruct(v)
|
||||
|
||||
case reflect.Func:
|
||||
s.dumpFunc(v)
|
||||
|
||||
case reflect.Chan:
|
||||
s.dumpChan(v)
|
||||
|
||||
default:
|
||||
if v.CanInterface() {
|
||||
s.writeString(fmt.Sprintf("%v", v.Interface()))
|
||||
} else {
|
||||
s.writeString(fmt.Sprintf("%v", v.String()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// registers that the value has been visited and checks to see if it is one of the
|
||||
// pointers we will see multiple times. If it is, it returns a temporary name for this
|
||||
// pointer. It also returns a boolean value indicating whether this is the first time
|
||||
// this name is returned so the caller can decide whether the contents of the pointer
|
||||
// has been dumped before or not.
|
||||
func (s *dumpState) pointerFor(v reflect.Value) (*ptrinfo, bool) {
|
||||
if isPointerValue(v) {
|
||||
if info, ok := s.pointers.get(v); ok {
|
||||
firstVisit := s.visitedPointers.add(v)
|
||||
return info, firstVisit
|
||||
}
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// prepares a new state object for dumping the provided value
|
||||
func newDumpState(value interface{}, options *Options, writer io.Writer) *dumpState {
|
||||
result := &dumpState{
|
||||
config: options,
|
||||
pointers: mapReusedPointers(reflect.ValueOf(value)),
|
||||
w: writer,
|
||||
}
|
||||
|
||||
if options.HomePackage != "" {
|
||||
result.homePackageRegexp = regexp.MustCompile(fmt.Sprintf("\\b%s\\.", options.HomePackage))
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// Dump a value to stdout
|
||||
func Dump(value ...interface{}) {
|
||||
(&Config).Dump(value...)
|
||||
}
|
||||
|
||||
// Sdump dumps a value to a string
|
||||
func Sdump(value ...interface{}) string {
|
||||
return (&Config).Sdump(value...)
|
||||
}
|
||||
|
||||
// Dump a value to stdout according to the options
|
||||
func (o Options) Dump(values ...interface{}) {
|
||||
for i, value := range values {
|
||||
state := newDumpState(value, &o, os.Stdout)
|
||||
if i > 0 {
|
||||
state.write([]byte(o.Separator))
|
||||
}
|
||||
state.dump(value)
|
||||
}
|
||||
_, _ = os.Stdout.Write([]byte("\n"))
|
||||
}
|
||||
|
||||
// Sdump dumps a value to a string according to the options
|
||||
func (o Options) Sdump(values ...interface{}) string {
|
||||
buf := new(bytes.Buffer)
|
||||
for i, value := range values {
|
||||
if i > 0 {
|
||||
_, _ = buf.Write([]byte(o.Separator))
|
||||
}
|
||||
state := newDumpState(value, &o, buf)
|
||||
state.dump(value)
|
||||
}
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
type mapKeySorter struct {
|
||||
keys []reflect.Value
|
||||
options *Options
|
||||
}
|
||||
|
||||
func (s mapKeySorter) Len() int {
|
||||
return len(s.keys)
|
||||
}
|
||||
|
||||
func (s mapKeySorter) Swap(i, j int) {
|
||||
s.keys[i], s.keys[j] = s.keys[j], s.keys[i]
|
||||
}
|
||||
|
||||
func (s mapKeySorter) Less(i, j int) bool {
|
||||
ibuf := new(bytes.Buffer)
|
||||
jbuf := new(bytes.Buffer)
|
||||
newDumpState(s.keys[i], s.options, ibuf).dumpVal(s.keys[i])
|
||||
newDumpState(s.keys[j], s.options, jbuf).dumpVal(s.keys[j])
|
||||
return ibuf.String() < jbuf.String()
|
||||
}
|
Reference in New Issue
Block a user