Du kan inte välja fler än 25 ämnen Ämnen måste starta med en bokstav eller siffra, kan innehålla bindestreck ('-') och vara max 35 tecken långa.
 
 
 

123 rader
4.0 KiB

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