debug_enable.go

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