debug_enable.go 4.0 KB

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