xref: /aosp_15_r20/external/go-cmp/cmp/internal/diff/debug_enable.go (revision 88d15eac089d7f20c739ff1001d56b91872b21a1)
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