github.com/mitranim/gg@v0.1.17/grepr/grepr_internal.go (about)

     1  package grepr
     2  
     3  import (
     4  	"fmt"
     5  	"math"
     6  	r "reflect"
     7  	"regexp"
     8  	"strconv"
     9  	"strings"
    10  	"unicode"
    11  	"unicode/utf8"
    12  
    13  	"github.com/mitranim/gg"
    14  )
    15  
    16  var (
    17  	typRtype      = gg.Type[r.Type]()
    18  	typBool       = gg.Type[bool]()
    19  	typInt        = gg.Type[int]()
    20  	typString     = gg.Type[string]()
    21  	typFloat64    = gg.Type[float64]()
    22  	typComplex64  = gg.Type[complex64]()
    23  	typComplex128 = gg.Type[complex128]()
    24  	typGoStringer = gg.Type[fmt.GoStringer]()
    25  )
    26  
    27  func (self *Fmt) fmtAny(typ r.Type, src r.Value) {
    28  	if self.fmtedNil(typ, src) || self.fmtedGoString(src) {
    29  		return
    30  	}
    31  
    32  	if typ == typRtype {
    33  		self.fmtReflectType(src)
    34  		return
    35  	}
    36  
    37  	switch src.Kind() {
    38  	case r.Invalid:
    39  		self.fmtNil(typ, src)
    40  	case r.Bool:
    41  		self.fmtBool(typ, src)
    42  	case r.Int8:
    43  		self.fmtInt64(typ, src)
    44  	case r.Int16:
    45  		self.fmtInt64(typ, src)
    46  	case r.Int32:
    47  		self.fmtInt64(typ, src)
    48  	case r.Int64:
    49  		self.fmtInt64(typ, src)
    50  	case r.Int:
    51  		self.fmtInt64(typ, src)
    52  	case r.Uint8:
    53  		self.fmtByteHex(typ, src)
    54  	case r.Uint16:
    55  		self.fmtUint64(typ, src)
    56  	case r.Uint32:
    57  		self.fmtUint64(typ, src)
    58  	case r.Uint64:
    59  		self.fmtUint64(typ, src)
    60  	case r.Uint:
    61  		self.fmtUint64(typ, src)
    62  	case r.Uintptr:
    63  		self.fmtUintHex(typ, src)
    64  	case r.Float32:
    65  		self.fmtFloat64(typ, src)
    66  	case r.Float64:
    67  		self.fmtFloat64(typ, src)
    68  	case r.Complex64:
    69  		self.fmtComplex(typ, src)
    70  	case r.Complex128:
    71  		self.fmtComplex(typ, src)
    72  	case r.Array:
    73  		self.fmtArray(src)
    74  	case r.Slice:
    75  		self.fmtSlice(typ, src)
    76  	case r.Chan:
    77  		self.fmtChan(typ, src)
    78  	case r.Func:
    79  		self.fmtFunc(typ, src)
    80  	case r.Interface:
    81  		self.fmtIface(typ, src)
    82  	case r.Map:
    83  		self.fmtMap(typ, src)
    84  	case r.Pointer:
    85  		self.fmtPointer(typ, src)
    86  	case r.UnsafePointer:
    87  		self.fmtUnsafePointer(typ, src)
    88  	case r.String:
    89  		self.fmtString(typ, src)
    90  	case r.Struct:
    91  		self.fmtStruct(src)
    92  	default:
    93  		panic(gg.Errf(`unrecognized reflect kind %q`, src.Kind()))
    94  	}
    95  }
    96  
    97  func (self *Fmt) fmtedPointerVisited(typ r.Type, src r.Value) bool {
    98  	if src.IsNil() {
    99  		self.fmtNil(typ, src)
   100  		return true
   101  	}
   102  
   103  	ptr := src.UnsafePointer()
   104  	_, ok := self.Visited[ptr]
   105  	if ok {
   106  		self.fmtPointerVisited(src)
   107  		return true
   108  	}
   109  
   110  	self.Visited.Init().Add(ptr)
   111  	return false
   112  }
   113  
   114  func (self *Fmt) fmtPointerVisited(src r.Value) {
   115  	self.AppendString(`/* visited */ `)
   116  	self.fmtType(src.Type())
   117  	self.AppendByte('(')
   118  	self.fmtUint64Hex(uint64(src.Pointer()))
   119  	self.AppendByte(')')
   120  }
   121  
   122  func (self *Fmt) fmtedNil(typ r.Type, src r.Value) bool {
   123  	if !src.IsValid() || gg.IsValueNil(src) {
   124  		self.fmtNil(typ, src)
   125  		return true
   126  	}
   127  	return false
   128  }
   129  
   130  func (self *Fmt) fmtNil(typ r.Type, src r.Value) {
   131  	srcTyp := gg.ValueType(src)
   132  
   133  	// TODO simplify.
   134  	if !(isTypeInterface(typ) && isValueNilInterface(src) ||
   135  		typ == srcTyp && gg.IsTypeNilable(typ)) {
   136  		defer self.fmtConvOpen(derefIface(src).Type()).fmtConvClose()
   137  	}
   138  
   139  	self.AppendString(`nil`)
   140  }
   141  
   142  /*
   143  TODO: consider custom interface such as `.AppendGoString`, possibly with
   144  indentation support.
   145  
   146  TODO: if, rather than implementing `.GoString` directly, the input inherits the
   147  method from an embedded type, we should do nothing and return false.
   148  */
   149  func (self *Fmt) fmtedGoString(src r.Value) bool {
   150  	if !src.Type().Implements(typGoStringer) {
   151  		return false
   152  	}
   153  	self.AppendString(src.Interface().(fmt.GoStringer).GoString())
   154  	return true
   155  }
   156  
   157  func (self *Fmt) fmtBool(typ r.Type, src r.Value) {
   158  	defer self.fmtConvOpt(typ, src.Type(), typBool).fmtConvClose()
   159  	self.AppendBool(src.Bool())
   160  }
   161  
   162  /*
   163  Adapted from `strconv.FormatComplex` with minor changes. We don't bother
   164  printing the real part when it's zero, and we avoid the scientific notation
   165  when formatting floats.
   166  */
   167  func (self *Fmt) fmtComplex(typ r.Type, src r.Value) {
   168  	done := self.fmtConvOpt(typ, src.Type(), typComplex128)
   169  	if done != nil {
   170  		defer done.fmtConvClose()
   171  	} else {
   172  		self.AppendByte('(')
   173  		defer self.AppendByte(')')
   174  	}
   175  
   176  	val := src.Complex()
   177  	realPart := real(val)
   178  	imagPart := imag(val)
   179  
   180  	if realPart == 0 {
   181  		self.AppendFloat64(imagPart)
   182  		self.AppendByte('i')
   183  		return
   184  	}
   185  
   186  	self.AppendFloat64(realPart)
   187  	if !(imagPart < 0) {
   188  		self.AppendByte('+')
   189  	}
   190  
   191  	self.AppendFloat64(imagPart)
   192  	self.AppendByte('i')
   193  }
   194  
   195  func (self *Fmt) fmtInt64(typ r.Type, src r.Value) {
   196  	defer self.fmtConvOpt(typ, src.Type(), typInt).fmtConvClose()
   197  	self.AppendInt64(src.Int())
   198  }
   199  
   200  func (self *Fmt) fmtUint64(typ r.Type, src r.Value) {
   201  	defer self.fmtConvOpt(typ, src.Type()).fmtConvClose()
   202  	self.AppendUint64(src.Uint())
   203  }
   204  
   205  func (self *Fmt) fmtByteHex(typ r.Type, src r.Value) {
   206  	defer self.fmtConvOpt(typ, src.Type()).fmtConvClose()
   207  	self.AppendString(`0x`)
   208  	self.AppendByteHex(byte(src.Uint()))
   209  }
   210  
   211  func (self *Fmt) fmtUintHex(typ r.Type, src r.Value) {
   212  	defer self.fmtConvOpt(typ, src.Type()).fmtConvClose()
   213  	self.fmtUint64Hex(src.Uint())
   214  }
   215  
   216  func (self *Fmt) fmtFloat64(typ r.Type, src r.Value) {
   217  	val := src.Float()
   218  	srcTyp := src.Type()
   219  	if isTypeInterface(typ) && (srcTyp != typFloat64 || !(math.Remainder(val, 1) > 0)) {
   220  		defer self.fmtConvOpen(srcTyp).fmtConvClose()
   221  	}
   222  
   223  	if val < 0 {
   224  		if math.IsInf(val, -1) {
   225  			self.AppendString(`math.Inf(-1)`)
   226  			return
   227  		}
   228  		self.AppendFloat64(val)
   229  		return
   230  	}
   231  
   232  	if val >= 0 {
   233  		if math.IsInf(val, 1) {
   234  			self.AppendString(`math.Inf(0)`)
   235  			return
   236  		}
   237  		self.AppendFloat64(val)
   238  		return
   239  	}
   240  
   241  	self.AppendString(`math.NaN()`)
   242  }
   243  
   244  func (self *Fmt) fmtString(typ r.Type, src r.Value) {
   245  	defer self.fmtConvOpt(typ, src.Type(), typString).fmtConvClose()
   246  	text := src.String()
   247  	self.fmtStringInner(text, textPrintability(text))
   248  }
   249  
   250  func (self *Fmt) fmtStringInner(src string, prn printability) {
   251  	/**
   252  	For the most part we're more restrictive than `strconv.CanBackquote`, but
   253  	unlike `strconv.CanBackquote` we allow '\n' in backquoted strings. We want
   254  	to avoid loss of information when a multiline string is displayed in a
   255  	terminal and copied to an editor. `strconv.CanBackquote` allows too many
   256  	characters which may fail to display properly. This includes the tabulation
   257  	character '\t', which is usually converted to spaces, and a variety of
   258  	Unicode code points without corresponding graphical symbols. On the other
   259  	hand, we want to support multiline strings in the common case, without edge
   260  	case breakage. We assume that terminals, and other means of displaying the
   261  	output of a program that may be using `grepr`, do not convert printed '\n'
   262  	to '\r' or "\r\n", but may convert printed '\r' or "\r\n" to '\n', as Unix
   263  	line endings tend to be preferred by any tooling used by Go developers. This
   264  	allows us to support displaying strings as multiline in the common case,
   265  	while avoiding information loss in the case of strings with '\r'.
   266  	*/
   267  	if !prn.errors && !prn.unprintables && !prn.backquotes && !prn.carriageReturns && (!prn.lineFeeds || self.IsMulti()) {
   268  		self.AppendByte('`')
   269  		self.AppendString(src)
   270  		self.AppendByte('`')
   271  	} else {
   272  		self.Buf = strconv.AppendQuote(self.Buf, src)
   273  	}
   274  }
   275  
   276  func (self *Fmt) fmtSlice(typ r.Type, src r.Value) {
   277  	if self.fmtedNil(typ, src) {
   278  		return
   279  	}
   280  
   281  	if gg.IsValueBytes(src) {
   282  		self.fmtBytes(typ, src)
   283  		return
   284  	}
   285  
   286  	self.fmtArray(src)
   287  }
   288  
   289  func (self *Fmt) fmtArray(src r.Value) {
   290  	typ := src.Type()
   291  
   292  	self.fmtTypeOpt(typ)
   293  	defer self.setElideType(!isTypeInterface(typ.Elem())).Done()
   294  
   295  	if src.Len() == 0 || src.IsZero() {
   296  		self.AppendString(`{}`)
   297  		return
   298  	}
   299  
   300  	if self.IsSingle() {
   301  		self.fmtArraySingle(typ, src)
   302  		return
   303  	}
   304  
   305  	self.fmtArrayMulti(typ, src)
   306  }
   307  
   308  func (self *Fmt) fmtArraySingle(typ r.Type, src r.Value) {
   309  	typElem := typ.Elem()
   310  
   311  	self.AppendByte('{')
   312  	for ind := range gg.Iter(src.Len()) {
   313  		if ind > 0 {
   314  			self.AppendString(`, `)
   315  		}
   316  		self.fmtAny(typElem, src.Index(ind))
   317  	}
   318  	self.AppendByte('}')
   319  }
   320  
   321  func (self *Fmt) fmtArrayMulti(typ r.Type, src r.Value) {
   322  	typElem := typ.Elem()
   323  
   324  	self.AppendByte('{')
   325  	self.AppendNewline()
   326  	snap := self.lvlInc()
   327  
   328  	for ind := range gg.Iter(src.Len()) {
   329  		self.fmtIndent()
   330  		self.fmtAny(typElem, src.Index(ind))
   331  		self.AppendByte(',')
   332  		self.AppendNewline()
   333  	}
   334  
   335  	snap.Done()
   336  	self.fmtIndent()
   337  	self.AppendByte('}')
   338  }
   339  
   340  func (self *Fmt) fmtBytes(typ r.Type, src r.Value) {
   341  	if self.fmtedNil(typ, src) {
   342  		return
   343  	}
   344  
   345  	text := src.Bytes()
   346  
   347  	if len(text) > 0 {
   348  		prn := textPrintability(text)
   349  
   350  		if !prn.errors && !prn.unprintables {
   351  			self.fmtType(src.Type())
   352  			self.AppendByte('(')
   353  			self.fmtStringInner(gg.ToString(text), prn)
   354  			self.AppendByte(')')
   355  			return
   356  		}
   357  	}
   358  
   359  	self.fmtBytesHex(src.Type(), text)
   360  }
   361  
   362  /*
   363  Similar to `.fmtArray`, but much faster and always single-line. TODO consider
   364  supporting column width in `Conf`, which would allow us to print bytes in
   365  rows.
   366  */
   367  func (self *Fmt) fmtBytesHex(typ r.Type, src []byte) {
   368  	self.fmtTypeOpt(typ)
   369  	self.AppendByte('{')
   370  	for ind, val := range src {
   371  		if ind > 0 {
   372  			self.AppendString(`, `)
   373  		}
   374  		self.AppendString(`0x`)
   375  		self.AppendByteHex(val)
   376  	}
   377  	self.AppendByte('}')
   378  }
   379  
   380  func (self *Fmt) fmtChan(typ r.Type, src r.Value) {
   381  	self.fmtUnfmtable(typ, src)
   382  }
   383  
   384  func (self *Fmt) fmtFunc(typ r.Type, src r.Value) {
   385  	self.fmtUnfmtable(typ, src)
   386  }
   387  
   388  func (self *Fmt) fmtIface(typ r.Type, src r.Value) {
   389  	if self.fmtedNil(typ, src) {
   390  		return
   391  	}
   392  	self.fmtAny(typ, src.Elem())
   393  }
   394  
   395  func (self *Fmt) fmtMap(typ r.Type, src r.Value) {
   396  	if self.fmtedNil(typ, src) {
   397  		return
   398  	}
   399  
   400  	srcTyp := src.Type()
   401  
   402  	self.fmtTypeOpt(srcTyp)
   403  	defer self.setElideType(!isTypeInterface(srcTyp.Elem())).Done()
   404  
   405  	if src.Len() == 0 {
   406  		self.AppendString(`{}`)
   407  		return
   408  	}
   409  
   410  	if self.IsSingle() {
   411  		self.fmtMapSingle(src)
   412  		return
   413  	}
   414  
   415  	self.fmtMapMulti(src)
   416  }
   417  
   418  func (self *Fmt) fmtMapSingle(src r.Value) {
   419  	typ := src.Type()
   420  	typKey := typ.Key()
   421  	typVal := typ.Elem()
   422  
   423  	self.AppendByte('{')
   424  
   425  	iter := src.MapRange()
   426  	var found bool
   427  
   428  	for iter.Next() {
   429  		if found {
   430  			self.AppendString(`, `)
   431  		}
   432  		found = true
   433  
   434  		self.fmtAny(typKey, iter.Key())
   435  		self.AppendString(`: `)
   436  		self.fmtAny(typVal, iter.Value())
   437  	}
   438  
   439  	self.AppendByte('}')
   440  }
   441  
   442  func (self *Fmt) fmtMapMulti(src r.Value) {
   443  	typ := src.Type()
   444  	typKey := typ.Key()
   445  	typVal := typ.Elem()
   446  
   447  	self.AppendByte('{')
   448  	self.AppendNewline()
   449  
   450  	iter := src.MapRange()
   451  	snap := self.lvlInc()
   452  
   453  	for iter.Next() {
   454  		self.fmtIndent()
   455  		self.fmtAny(typKey, iter.Key())
   456  		self.AppendString(`: `)
   457  		self.fmtAny(typVal, iter.Value())
   458  		self.AppendByte(',')
   459  		self.AppendNewline()
   460  	}
   461  
   462  	snap.Done()
   463  	self.fmtIndent()
   464  	self.AppendByte('}')
   465  }
   466  
   467  func (self *Fmt) fmtPointer(typ r.Type, src r.Value) {
   468  	if self.fmtedNil(typ, src) || self.fmtedPointerVisited(typ, src) {
   469  		return
   470  	}
   471  
   472  	defer self.setElideType(false).Done()
   473  	src = src.Elem()
   474  
   475  	if canAmpersand(src.Kind()) {
   476  		self.AppendByte('&')
   477  		self.fmtAny(typ, src)
   478  		return
   479  	}
   480  
   481  	self.fmtIdent(`gg`, `Ptr`)
   482  	self.fmtTypeArg(src.Type())
   483  	self.AppendByte('(')
   484  	self.fmtAny(typ, src)
   485  	self.AppendByte(')')
   486  }
   487  
   488  func (self *Fmt) fmtUnsafePointer(typ r.Type, src r.Value) {
   489  	defer self.fmtConvOpt(typ, src.Type()).fmtConvClose()
   490  	self.fmtUint64Hex(uint64(src.Pointer()))
   491  }
   492  
   493  func (self *Fmt) fmtStruct(src r.Value) {
   494  	self.fmtTypeOpt(src.Type())
   495  	defer self.setElideType(false).Done()
   496  
   497  	if src.NumField() == 0 {
   498  		self.AppendString(`{}`)
   499  		return
   500  	}
   501  
   502  	if self.IsSingle() {
   503  		self.fmtStructSingle(src)
   504  		return
   505  	}
   506  
   507  	self.fmtStructMulti(src)
   508  }
   509  
   510  func (self *Fmt) fmtStructField(src r.Value, field r.StructField) {
   511  	self.AppendString(field.Name)
   512  	self.AppendString(`: `)
   513  	self.fmtAny(field.Type, src)
   514  }
   515  
   516  func (self *Fmt) fmtStructSingle(src r.Value) {
   517  	if isStructUnit(src.Type()) {
   518  		self.fmtStructSingleAnon(src)
   519  		return
   520  	}
   521  	self.fmtStructSingleNamed(src)
   522  }
   523  
   524  func (self *Fmt) fmtStructSingleAnon(src r.Value) {
   525  	head := src.Field(0)
   526  
   527  	self.AppendByte('{')
   528  	if !self.skipField(head) {
   529  		self.fmtAny(structHeadType(src.Type()), head)
   530  	}
   531  	self.AppendByte('}')
   532  }
   533  
   534  func (self *Fmt) fmtStructSingleNamed(src r.Value) {
   535  	self.AppendByte('{')
   536  
   537  	var found bool
   538  
   539  	for _, field := range gg.StructPublicFieldCache.Get(src.Type()) {
   540  		src := src.FieldByIndex(field.Index)
   541  		if self.skipField(src) {
   542  			continue
   543  		}
   544  
   545  		if found {
   546  			self.AppendString(`, `)
   547  		}
   548  		found = true
   549  
   550  		self.fmtStructField(src, field)
   551  	}
   552  
   553  	self.AppendByte('}')
   554  }
   555  
   556  func (self *Fmt) fmtStructMulti(src r.Value) {
   557  	if isStructUnit(src.Type()) {
   558  		self.fmtStructMultiAnon(src)
   559  		return
   560  	}
   561  	self.fmtStructMultiNamed(src)
   562  }
   563  
   564  func (self *Fmt) fmtStructMultiAnon(src r.Value) {
   565  	head := src.Field(0)
   566  
   567  	self.AppendByte('{')
   568  
   569  	if !self.skipField(head) {
   570  		defer self.lvlInc().Done()
   571  		self.fmtAny(structHeadType(src.Type()), head)
   572  	}
   573  
   574  	self.AppendByte('}')
   575  }
   576  
   577  func (self *Fmt) fmtStructMultiNamed(src r.Value) {
   578  	fields := gg.StructPublicFieldCache.Get(src.Type())
   579  
   580  	if self.SkipZeroFields() {
   581  		test := func(field r.StructField) bool {
   582  			return !src.FieldByIndex(field.Index).IsZero()
   583  		}
   584  
   585  		count := gg.Count(fields, test)
   586  
   587  		if count == 0 {
   588  			self.AppendString(`{}`)
   589  			return
   590  		}
   591  
   592  		if count == 1 {
   593  			field := gg.Find(fields, test)
   594  			self.fmtStructMultiNamedUnit(src.FieldByIndex(field.Index), field)
   595  			return
   596  		}
   597  	}
   598  
   599  	self.fmtStructMultiNamedLines(src, fields)
   600  }
   601  
   602  func (self *Fmt) fmtStructMultiNamedUnit(src r.Value, field r.StructField) {
   603  	self.AppendByte('{')
   604  	self.fmtStructField(src, field)
   605  	self.AppendByte('}')
   606  }
   607  
   608  func (self *Fmt) fmtStructMultiNamedLines(src r.Value, fields []r.StructField) {
   609  	self.AppendByte('{')
   610  	self.AppendNewline()
   611  	snap := self.lvlInc()
   612  
   613  	for _, field := range fields {
   614  		src := src.FieldByIndex(field.Index)
   615  		if self.skipField(src) {
   616  			continue
   617  		}
   618  
   619  		self.fmtIndent()
   620  		self.fmtStructField(src, field)
   621  		self.AppendByte(',')
   622  		self.AppendNewline()
   623  	}
   624  
   625  	snap.Done()
   626  	self.fmtIndent()
   627  	self.AppendByte('}')
   628  }
   629  
   630  func (self *Fmt) fmtUnfmtable(typ r.Type, src r.Value) {
   631  	if self.fmtedNil(typ, src) {
   632  		return
   633  	}
   634  
   635  	self.fmtType(src.Type())
   636  	self.AppendByte('(')
   637  	self.fmtUint64Hex(uint64(src.Pointer()))
   638  	self.AppendByte(')')
   639  }
   640  
   641  func (self *Fmt) fmtTypeArg(typ r.Type) {
   642  	if isTypeDefaultForLiteral(typ) {
   643  		return
   644  	}
   645  
   646  	self.AppendByte('[')
   647  	self.fmtType(typ)
   648  	self.AppendByte(']')
   649  }
   650  
   651  func (self *Fmt) fmtReflectType(src r.Value) {
   652  	self.fmtIdent(`gg`, `Type`)
   653  	self.AppendByte('[')
   654  	self.fmtType(src.Interface().(r.Type))
   655  	self.AppendString(`]()`)
   656  }
   657  
   658  func (self *Fmt) fmtUint64Hex(val uint64) {
   659  	self.AppendString(`0x`)
   660  	self.AppendUint64Hex(val)
   661  }
   662  
   663  func (self *Fmt) fmtIndent() { self.AppendStringN(self.Indent, self.Lvl) }
   664  
   665  func (self *Fmt) fmtIdent(pkg, name string) {
   666  	if self.Pkg != pkg {
   667  		self.AppendString(pkg)
   668  		self.AppendByte('.')
   669  	}
   670  	self.AppendString(name)
   671  }
   672  
   673  func (self *Fmt) fmtTypeOpt(typ r.Type) {
   674  	if !self.ElideType {
   675  		self.fmtType(typ)
   676  	}
   677  }
   678  
   679  func (self *Fmt) fmtType(typ r.Type) {
   680  	self.AppendString(self.typeString(typ))
   681  }
   682  
   683  func (self *Fmt) fmtConvOpen(typ r.Type) *Fmt {
   684  	self.fmtType(typ)
   685  	self.AppendByte('(')
   686  	return self
   687  }
   688  
   689  func (self *Fmt) fmtConvOpt(outer, inner r.Type, excl ...r.Type) *Fmt {
   690  	if !isTypeInterface(outer) || gg.Has(excl, inner) {
   691  		return nil
   692  	}
   693  	return self.fmtConvOpen(inner)
   694  }
   695  
   696  func (self *Fmt) fmtConvClose() {
   697  	// The nil check is relevant for `defer`. See `.fmtConvOpt`.
   698  	if self != nil {
   699  		self.AppendByte(')')
   700  	}
   701  }
   702  
   703  func (self *Fmt) setElideType(val bool) gg.Snapshot[bool] {
   704  	return gg.SnapSwap(&self.ElideType, val)
   705  }
   706  
   707  func (self *Fmt) lvlInc() gg.Snapshot[int] {
   708  	return gg.SnapSwap(&self.Lvl, self.Lvl+1)
   709  }
   710  
   711  func (self *Fmt) skipField(src r.Value) bool {
   712  	return self.SkipZeroFields() && src.IsZero()
   713  }
   714  
   715  func (self *Fmt) typeString(typ r.Type) string {
   716  	return self.elidePkg(typeString(typ))
   717  }
   718  
   719  /*
   720  Known issue: this works only for `pkg.ident` but not for `[]pkg.ident`,
   721  `map[pkg.ident]pkg.ident`, `func(pkg.ident)`, and so on. We should either
   722  implement proper rewriting that works in all cases, or remove this feature.
   723  */
   724  func (self *Fmt) elidePkg(src string) string {
   725  	pkg := self.Pkg
   726  	if pkg == `` {
   727  		return src
   728  	}
   729  
   730  	tar := strings.TrimPrefix(src, pkg)
   731  	if len(src) != len(tar) && len(tar) > 0 && tar[0] == '.' {
   732  		return tar[1:]
   733  	}
   734  	return src
   735  }
   736  
   737  func typeString(typ r.Type) string { return string(typeStringCache.Get(typ)) }
   738  
   739  var typeStringCache = gg.TypeCacheOf[typeStringStr]()
   740  
   741  type typeStringStr string
   742  
   743  func (self *typeStringStr) Init(typ r.Type) {
   744  	if typ == nil {
   745  		return
   746  	}
   747  
   748  	tar := typ.String()
   749  
   750  	/**
   751  	Some types must be wrapped in parens because we use the resulting type name
   752  	in expression context, not in type context. Wrapping avoids ambiguity with
   753  	value expression syntax.
   754  	*/
   755  	if typ.Kind() == r.Func && strings.HasPrefix(tar, `func(`) ||
   756  		typ.Kind() == r.Pointer && strings.HasPrefix(tar, `*`) {
   757  		tar = `(` + tar + `)`
   758  	}
   759  
   760  	tar = strings.ReplaceAll(tar, `interface {}`, `any`)
   761  	tar = reUint8.Get().ReplaceAllString(tar, `byte`)
   762  	*self = typeStringStr(tar)
   763  }
   764  
   765  var reUint8 = gg.NewLazy(func() *regexp.Regexp {
   766  	return regexp.MustCompile(`\buint8\b`)
   767  })
   768  
   769  func canAmpersand(kind r.Kind) bool {
   770  	return kind == r.Array || kind == r.Slice || kind == r.Struct
   771  }
   772  
   773  func isTypeDefaultForLiteral(typ r.Type) bool {
   774  	switch typ {
   775  	case nil, typBool, typInt, typString, typComplex64, typComplex128:
   776  		return true
   777  	default:
   778  		return false
   779  	}
   780  }
   781  
   782  func isValueNilInterface(src r.Value) bool {
   783  	return !src.IsValid() || isValueInterface(src) && src.IsNil()
   784  }
   785  
   786  func isValueInterface(src r.Value) bool {
   787  	return src.Kind() == r.Interface
   788  }
   789  
   790  func isStructUnit(typ r.Type) bool { return typ.NumField() == 1 }
   791  
   792  func isTypeInterface(typ r.Type) bool { return gg.TypeKind(typ) == r.Interface }
   793  
   794  func structHeadType(typ r.Type) r.Type {
   795  	return gg.StructFieldCache.Get(typ)[0].Type
   796  }
   797  
   798  func derefIface(val r.Value) r.Value {
   799  	for val.Kind() == r.Interface {
   800  		val = val.Elem()
   801  	}
   802  	return val
   803  }
   804  
   805  func textPrintability[A gg.Text](src A) (out printability) {
   806  	out.init(gg.ToString(src))
   807  	return
   808  }
   809  
   810  type printability struct {
   811  	errors, lineFeeds, carriageReturns, backquotes, escapes, unprintables bool
   812  }
   813  
   814  func (self *printability) init(src string) {
   815  	for _, val := range src {
   816  		/**
   817  		`unicode.IsPrint` uses `unicode.S` which includes `utf8.RuneError`.
   818  		As a result, it considers error runes printable, which is wildly
   819  		inappropriate for our purposes. So we have to handle it separately.
   820  		*/
   821  		if val == utf8.RuneError {
   822  			self.errors = true
   823  			return
   824  		}
   825  
   826  		if val == '\n' {
   827  			self.lineFeeds = true
   828  			continue
   829  		}
   830  
   831  		if val == '\r' {
   832  			self.carriageReturns = true
   833  			continue
   834  		}
   835  
   836  		if val == '`' {
   837  			self.backquotes = true
   838  			continue
   839  		}
   840  
   841  		if int(val) < len(stringEsc) && stringEsc[byte(val)] {
   842  			self.escapes = true
   843  			continue
   844  		}
   845  
   846  		if !unicode.IsPrint(val) {
   847  			self.unprintables = true
   848  			return
   849  		}
   850  	}
   851  }
   852  
   853  // https://go.dev/ref/spec#String_literals
   854  var stringEsc = [256]bool{
   855  	byte('\a'): true,
   856  	byte('\b'): true,
   857  	byte('\f'): true,
   858  	byte('\n'): true,
   859  	byte('\r'): true,
   860  	byte('\t'): true,
   861  	byte('\v'): true,
   862  	byte('\\'): true,
   863  	byte('"'):  true,
   864  }