package cmp
import (
)
var randBool = rand.New(rand.NewSource(time.Now().Unix())).Intn(2) == 0
const maxColumnLength = 80
type indentMode int
func ( indentMode) ( []byte, diffMode) []byte {
if flags.Deterministic || randBool {
switch {
case diffUnknown, diffIdentical:
= append(, " "...)
case diffRemoved:
= append(, "- "...)
case diffInserted:
= append(, "+ "...)
}
} else {
switch {
case diffUnknown, diffIdentical:
= append(, " "...)
case diffRemoved:
= append(, "- "...)
case diffInserted:
= append(, "+ "...)
}
}
return repeatCount().appendChar(, '\t')
}
type repeatCount int
func ( repeatCount) ( []byte, byte) []byte {
for ; > 0; -- {
= append(, )
}
return
}
type textNode interface {
Len() int
Equal(textNode) bool
String() string
formatCompactTo([]byte, diffMode) ([]byte, textNode)
formatExpandedTo([]byte, diffMode, indentMode) []byte
}
type textWrap struct {
Prefix string
Value textNode
Suffix string
Metadata interface{}
}
func ( *textWrap) () int {
return len(.Prefix) + .Value.Len() + len(.Suffix)
}
func ( *textWrap) ( textNode) bool {
if , := .(*textWrap); {
return .Prefix == .Prefix && .Value.Equal(.Value) && .Suffix == .Suffix
}
return false
}
func ( *textWrap) () string {
var diffMode
var indentMode
, := .formatCompactTo(nil, )
:= .appendIndent(nil, )
= .formatExpandedTo(, , )
= append(, '\n')
return string()
}
func ( *textWrap) ( []byte, diffMode) ([]byte, textNode) {
:= len()
= append(, .Prefix...)
, .Value = .Value.formatCompactTo(, )
= append(, .Suffix...)
if , := .Value.(textLine); {
return , textLine([:])
}
return ,
}
func ( *textWrap) ( []byte, diffMode, indentMode) []byte {
= append(, .Prefix...)
= .Value.formatExpandedTo(, , )
= append(, .Suffix...)
return
}
type textList []textRecord
type textRecord struct {
Diff diffMode
Key string
Value textNode
ElideComma bool
Comment fmt.Stringer
}
func ( *textList) ( diffStats) {
:= !.IsZero()
if len(*) == 0 || !(*)[len(*)-1].Value.Equal(textEllipsis) {
if {
* = append(*, textRecord{Value: textEllipsis, ElideComma: true, Comment: })
} else {
* = append(*, textRecord{Value: textEllipsis, ElideComma: true})
}
return
}
if {
(*)[len(*)-1].Comment = (*)[len(*)-1].Comment.(diffStats).Append()
}
}
func ( textList) () ( int) {
for , := range {
+= len(.Key)
if .Key != "" {
+= len(": ")
}
+= .Value.Len()
if < len()-1 {
+= len(", ")
}
}
return
}
func ( textList) ( textNode) bool {
if , := .(textList); {
if len() != len() {
return false
}
for := range {
, := [], []
if !(.Diff == .Diff && .Key == .Key && .Value.Equal(.Value) && .Comment == .Comment) {
return false
}
}
return true
}
return false
}
func ( textList) () string {
return (&textWrap{Prefix: "{", Value: , Suffix: "}"}).String()
}
func ( textList) ( []byte, diffMode) ([]byte, textNode) {
= append(textList(nil), ...)
:= len()
var bool
for , := range {
if .Diff == diffInserted || .Diff == diffRemoved {
= true
}
= append(, .Key...)
if .Key != "" {
= append(, ": "...)
}
, [].Value = .Value.formatCompactTo(, |.Diff)
if , := [].Value.(textLine); ! {
= true
}
if .Comment != nil {
= true
}
if < len()-1 {
= append(, ", "...)
}
}
if ( == diffInserted || == diffRemoved) && len([:]) > maxColumnLength {
= true
}
if ! {
return , textLine([:])
}
return ,
}
func ( textList) ( []byte, diffMode, indentMode) []byte {
:= .alignLens(
func( textRecord) bool {
, := .Value.(textLine)
return .Key == "" || !
},
func( textRecord) int { return utf8.RuneCountInString(.Key) },
)
:= .alignLens(
func( textRecord) bool {
, := .Value.(textLine)
return ! || .Value.Equal(textEllipsis) || .Comment == nil
},
func( textRecord) int { return utf8.RuneCount(.Value.(textLine)) },
)
var bool
for , := range {
, := .Value.(textLine)
= .Diff == 0 && .Key == "" && && .Comment == nil
if ! {
break
}
}
if {
++
var []byte
:= func() {
if len() > 0 {
= .appendIndent(append(, '\n'), )
= append(, bytes.TrimRight(, " ")...)
= [:0]
}
}
for , := range {
:= .Value.(textLine)
if len()+len()+len(", ") > maxColumnLength {
()
}
= append(, ...)
= append(, ", "...)
}
()
--
return .appendIndent(append(, '\n'), )
}
++
for , := range {
= .appendIndent(append(, '\n'), |.Diff)
if .Key != "" {
= append(, .Key+": "...)
}
= [].appendChar(, ' ')
= .Value.formatExpandedTo(, |.Diff, )
if !.ElideComma {
= append(, ',')
}
= [].appendChar(, ' ')
if .Comment != nil {
= append(, " // "+.Comment.String()...)
}
}
--
return .appendIndent(append(, '\n'), )
}
func ( textList) (
func(textRecord) bool,
func(textRecord) int,
) []repeatCount {
var , , int
:= make([]repeatCount, len())
for , := range {
if () {
for := ; < && < len(); ++ {
[] = repeatCount( - ([]))
}
, , = +1, +1, 0
} else {
if < () {
= ()
}
= + 1
}
}
for := ; < && < len(); ++ {
[] = repeatCount( - ([]))
}
return
}
type textLine []byte
var (
textNil = textLine("nil")
textEllipsis = textLine("...")
)
func ( textLine) () int {
return len()
}
func ( textLine) ( textNode) bool {
if , := .(textLine); {
return bytes.Equal([]byte(), []byte())
}
return false
}
func ( textLine) () string {
return string()
}
func ( textLine) ( []byte, diffMode) ([]byte, textNode) {
return append(, ...),
}
func ( textLine) ( []byte, diffMode, indentMode) []byte {
return append(, ...)
}
type diffStats struct {
Name string
NumIgnored int
NumIdentical int
NumRemoved int
NumInserted int
NumModified int
}
func ( diffStats) () bool {
.Name = ""
return == diffStats{}
}
func ( diffStats) () int {
return .NumRemoved + .NumInserted + .NumModified
}
func ( diffStats) ( diffStats) diffStats {
assert(.Name == .Name)
.NumIgnored += .NumIgnored
.NumIdentical += .NumIdentical
.NumRemoved += .NumRemoved
.NumInserted += .NumInserted
.NumModified += .NumModified
return
}
func ( diffStats) () string {
var []string
var int
:= [...]string{"ignored", "identical", "removed", "inserted", "modified"}
:= [...]int{.NumIgnored, .NumIdentical, .NumRemoved, .NumInserted, .NumModified}
for , := range {
if > 0 {
= append(, fmt.Sprintf("%d %v", , []))
}
+=
}
:= .Name
if > 1 {
+= "s"
if strings.HasSuffix(, "ys") {
= [:len()-2] + "ies"
}
}
switch := len(); {
case 0:
return ""
case 1, 2:
return strings.Join(, " and ") + " " +
default:
return strings.Join([:-1], ", ") + ", and " + [-1] + " " +
}
}
type commentString string
func ( commentString) () string { return string() }