1*88d15eacSSasha Smundak// Copyright 2017, The Go Authors. All rights reserved. 2*88d15eacSSasha Smundak// Use of this source code is governed by a BSD-style 3*88d15eacSSasha Smundak// license that can be found in the LICENSE file. 4*88d15eacSSasha Smundak 5*88d15eacSSasha Smundak//go:build cmp_debug 6*88d15eacSSasha Smundak// +build cmp_debug 7*88d15eacSSasha Smundak 8*88d15eacSSasha Smundakpackage diff 9*88d15eacSSasha Smundak 10*88d15eacSSasha Smundakimport ( 11*88d15eacSSasha Smundak "fmt" 12*88d15eacSSasha Smundak "strings" 13*88d15eacSSasha Smundak "sync" 14*88d15eacSSasha Smundak "time" 15*88d15eacSSasha Smundak) 16*88d15eacSSasha Smundak 17*88d15eacSSasha Smundak// The algorithm can be seen running in real-time by enabling debugging: 18*88d15eacSSasha Smundak// go test -tags=cmp_debug -v 19*88d15eacSSasha Smundak// 20*88d15eacSSasha Smundak// Example output: 21*88d15eacSSasha Smundak// === RUN TestDifference/#34 22*88d15eacSSasha Smundak// ┌───────────────────────────────┐ 23*88d15eacSSasha Smundak// │ \ · · · · · · · · · · · · · · │ 24*88d15eacSSasha Smundak// │ · # · · · · · · · · · · · · · │ 25*88d15eacSSasha Smundak// │ · \ · · · · · · · · · · · · · │ 26*88d15eacSSasha Smundak// │ · · \ · · · · · · · · · · · · │ 27*88d15eacSSasha Smundak// │ · · · X # · · · · · · · · · · │ 28*88d15eacSSasha Smundak// │ · · · # \ · · · · · · · · · · │ 29*88d15eacSSasha Smundak// │ · · · · · # # · · · · · · · · │ 30*88d15eacSSasha Smundak// │ · · · · · # \ · · · · · · · · │ 31*88d15eacSSasha Smundak// │ · · · · · · · \ · · · · · · · │ 32*88d15eacSSasha Smundak// │ · · · · · · · · \ · · · · · · │ 33*88d15eacSSasha Smundak// │ · · · · · · · · · \ · · · · · │ 34*88d15eacSSasha Smundak// │ · · · · · · · · · · \ · · # · │ 35*88d15eacSSasha Smundak// │ · · · · · · · · · · · \ # # · │ 36*88d15eacSSasha Smundak// │ · · · · · · · · · · · # # # · │ 37*88d15eacSSasha Smundak// │ · · · · · · · · · · # # # # · │ 38*88d15eacSSasha Smundak// │ · · · · · · · · · # # # # # · │ 39*88d15eacSSasha Smundak// │ · · · · · · · · · · · · · · \ │ 40*88d15eacSSasha Smundak// └───────────────────────────────┘ 41*88d15eacSSasha Smundak// [.Y..M.XY......YXYXY.|] 42*88d15eacSSasha Smundak// 43*88d15eacSSasha Smundak// The grid represents the edit-graph where the horizontal axis represents 44*88d15eacSSasha Smundak// list X and the vertical axis represents list Y. The start of the two lists 45*88d15eacSSasha Smundak// is the top-left, while the ends are the bottom-right. The '·' represents 46*88d15eacSSasha Smundak// an unexplored node in the graph. The '\' indicates that the two symbols 47*88d15eacSSasha Smundak// from list X and Y are equal. The 'X' indicates that two symbols are similar 48*88d15eacSSasha Smundak// (but not exactly equal) to each other. The '#' indicates that the two symbols 49*88d15eacSSasha Smundak// are different (and not similar). The algorithm traverses this graph trying to 50*88d15eacSSasha Smundak// make the paths starting in the top-left and the bottom-right connect. 51*88d15eacSSasha Smundak// 52*88d15eacSSasha Smundak// The series of '.', 'X', 'Y', and 'M' characters at the bottom represents 53*88d15eacSSasha Smundak// the currently established path from the forward and reverse searches, 54*88d15eacSSasha Smundak// separated by a '|' character. 55*88d15eacSSasha Smundak 56*88d15eacSSasha Smundakconst ( 57*88d15eacSSasha Smundak updateDelay = 100 * time.Millisecond 58*88d15eacSSasha Smundak finishDelay = 500 * time.Millisecond 59*88d15eacSSasha Smundak ansiTerminal = true // ANSI escape codes used to move terminal cursor 60*88d15eacSSasha Smundak) 61*88d15eacSSasha Smundak 62*88d15eacSSasha Smundakvar debug debugger 63*88d15eacSSasha Smundak 64*88d15eacSSasha Smundaktype debugger struct { 65*88d15eacSSasha Smundak sync.Mutex 66*88d15eacSSasha Smundak p1, p2 EditScript 67*88d15eacSSasha Smundak fwdPath, revPath *EditScript 68*88d15eacSSasha Smundak grid []byte 69*88d15eacSSasha Smundak lines int 70*88d15eacSSasha Smundak} 71*88d15eacSSasha Smundak 72*88d15eacSSasha Smundakfunc (dbg *debugger) Begin(nx, ny int, f EqualFunc, p1, p2 *EditScript) EqualFunc { 73*88d15eacSSasha Smundak dbg.Lock() 74*88d15eacSSasha Smundak dbg.fwdPath, dbg.revPath = p1, p2 75*88d15eacSSasha Smundak top := "┌─" + strings.Repeat("──", nx) + "┐\n" 76*88d15eacSSasha Smundak row := "│ " + strings.Repeat("· ", nx) + "│\n" 77*88d15eacSSasha Smundak btm := "└─" + strings.Repeat("──", nx) + "┘\n" 78*88d15eacSSasha Smundak dbg.grid = []byte(top + strings.Repeat(row, ny) + btm) 79*88d15eacSSasha Smundak dbg.lines = strings.Count(dbg.String(), "\n") 80*88d15eacSSasha Smundak fmt.Print(dbg) 81*88d15eacSSasha Smundak 82*88d15eacSSasha Smundak // Wrap the EqualFunc so that we can intercept each result. 83*88d15eacSSasha Smundak return func(ix, iy int) (r Result) { 84*88d15eacSSasha Smundak cell := dbg.grid[len(top)+iy*len(row):][len("│ ")+len("· ")*ix:][:len("·")] 85*88d15eacSSasha Smundak for i := range cell { 86*88d15eacSSasha Smundak cell[i] = 0 // Zero out the multiple bytes of UTF-8 middle-dot 87*88d15eacSSasha Smundak } 88*88d15eacSSasha Smundak switch r = f(ix, iy); { 89*88d15eacSSasha Smundak case r.Equal(): 90*88d15eacSSasha Smundak cell[0] = '\\' 91*88d15eacSSasha Smundak case r.Similar(): 92*88d15eacSSasha Smundak cell[0] = 'X' 93*88d15eacSSasha Smundak default: 94*88d15eacSSasha Smundak cell[0] = '#' 95*88d15eacSSasha Smundak } 96*88d15eacSSasha Smundak return 97*88d15eacSSasha Smundak } 98*88d15eacSSasha Smundak} 99*88d15eacSSasha Smundak 100*88d15eacSSasha Smundakfunc (dbg *debugger) Update() { 101*88d15eacSSasha Smundak dbg.print(updateDelay) 102*88d15eacSSasha Smundak} 103*88d15eacSSasha Smundak 104*88d15eacSSasha Smundakfunc (dbg *debugger) Finish() { 105*88d15eacSSasha Smundak dbg.print(finishDelay) 106*88d15eacSSasha Smundak dbg.Unlock() 107*88d15eacSSasha Smundak} 108*88d15eacSSasha Smundak 109*88d15eacSSasha Smundakfunc (dbg *debugger) String() string { 110*88d15eacSSasha Smundak dbg.p1, dbg.p2 = *dbg.fwdPath, dbg.p2[:0] 111*88d15eacSSasha Smundak for i := len(*dbg.revPath) - 1; i >= 0; i-- { 112*88d15eacSSasha Smundak dbg.p2 = append(dbg.p2, (*dbg.revPath)[i]) 113*88d15eacSSasha Smundak } 114*88d15eacSSasha Smundak return fmt.Sprintf("%s[%v|%v]\n\n", dbg.grid, dbg.p1, dbg.p2) 115*88d15eacSSasha Smundak} 116*88d15eacSSasha Smundak 117*88d15eacSSasha Smundakfunc (dbg *debugger) print(d time.Duration) { 118*88d15eacSSasha Smundak if ansiTerminal { 119*88d15eacSSasha Smundak fmt.Printf("\x1b[%dA", dbg.lines) // Reset terminal cursor 120*88d15eacSSasha Smundak } 121*88d15eacSSasha Smundak fmt.Print(dbg) 122*88d15eacSSasha Smundak time.Sleep(d) 123*88d15eacSSasha Smundak} 124