This starts the work of having a `NodeProvider` which is responsible for providing node details. It splits the responsibilities of node management off to a new controller. The primary change here is to add the framework pieces for node management and move the VK CLI to use this new controller. It also adds support for node leases where available. This can be enabled via the command line (disabled by default), but may fall back if we find that leaess aren't supported on the cluster.
162 lines
3.7 KiB
Go
162 lines
3.7 KiB
Go
package format
|
||
|
||
import (
|
||
"bytes"
|
||
"fmt"
|
||
"strings"
|
||
"unicode"
|
||
|
||
"gotest.tools/internal/difflib"
|
||
)
|
||
|
||
const (
|
||
contextLines = 2
|
||
)
|
||
|
||
// DiffConfig for a unified diff
|
||
type DiffConfig struct {
|
||
A string
|
||
B string
|
||
From string
|
||
To string
|
||
}
|
||
|
||
// UnifiedDiff is a modified version of difflib.WriteUnifiedDiff with better
|
||
// support for showing the whitespace differences.
|
||
func UnifiedDiff(conf DiffConfig) string {
|
||
a := strings.SplitAfter(conf.A, "\n")
|
||
b := strings.SplitAfter(conf.B, "\n")
|
||
groups := difflib.NewMatcher(a, b).GetGroupedOpCodes(contextLines)
|
||
if len(groups) == 0 {
|
||
return ""
|
||
}
|
||
|
||
buf := new(bytes.Buffer)
|
||
writeFormat := func(format string, args ...interface{}) {
|
||
buf.WriteString(fmt.Sprintf(format, args...))
|
||
}
|
||
writeLine := func(prefix string, s string) {
|
||
buf.WriteString(prefix + s)
|
||
}
|
||
if hasWhitespaceDiffLines(groups, a, b) {
|
||
writeLine = visibleWhitespaceLine(writeLine)
|
||
}
|
||
formatHeader(writeFormat, conf)
|
||
for _, group := range groups {
|
||
formatRangeLine(writeFormat, group)
|
||
for _, opCode := range group {
|
||
in, out := a[opCode.I1:opCode.I2], b[opCode.J1:opCode.J2]
|
||
switch opCode.Tag {
|
||
case 'e':
|
||
formatLines(writeLine, " ", in)
|
||
case 'r':
|
||
formatLines(writeLine, "-", in)
|
||
formatLines(writeLine, "+", out)
|
||
case 'd':
|
||
formatLines(writeLine, "-", in)
|
||
case 'i':
|
||
formatLines(writeLine, "+", out)
|
||
}
|
||
}
|
||
}
|
||
return buf.String()
|
||
}
|
||
|
||
// hasWhitespaceDiffLines returns true if any diff groups is only different
|
||
// because of whitespace characters.
|
||
func hasWhitespaceDiffLines(groups [][]difflib.OpCode, a, b []string) bool {
|
||
for _, group := range groups {
|
||
in, out := new(bytes.Buffer), new(bytes.Buffer)
|
||
for _, opCode := range group {
|
||
if opCode.Tag == 'e' {
|
||
continue
|
||
}
|
||
for _, line := range a[opCode.I1:opCode.I2] {
|
||
in.WriteString(line)
|
||
}
|
||
for _, line := range b[opCode.J1:opCode.J2] {
|
||
out.WriteString(line)
|
||
}
|
||
}
|
||
if removeWhitespace(in.String()) == removeWhitespace(out.String()) {
|
||
return true
|
||
}
|
||
}
|
||
return false
|
||
}
|
||
|
||
func removeWhitespace(s string) string {
|
||
var result []rune
|
||
for _, r := range s {
|
||
if !unicode.IsSpace(r) {
|
||
result = append(result, r)
|
||
}
|
||
}
|
||
return string(result)
|
||
}
|
||
|
||
func visibleWhitespaceLine(ws func(string, string)) func(string, string) {
|
||
mapToVisibleSpace := func(r rune) rune {
|
||
switch r {
|
||
case '\n':
|
||
case ' ':
|
||
return '·'
|
||
case '\t':
|
||
return '▷'
|
||
case '\v':
|
||
return '▽'
|
||
case '\r':
|
||
return '↵'
|
||
case '\f':
|
||
return '↓'
|
||
default:
|
||
if unicode.IsSpace(r) {
|
||
return '<27>'
|
||
}
|
||
}
|
||
return r
|
||
}
|
||
return func(prefix, s string) {
|
||
ws(prefix, strings.Map(mapToVisibleSpace, s))
|
||
}
|
||
}
|
||
|
||
func formatHeader(wf func(string, ...interface{}), conf DiffConfig) {
|
||
if conf.From != "" || conf.To != "" {
|
||
wf("--- %s\n", conf.From)
|
||
wf("+++ %s\n", conf.To)
|
||
}
|
||
}
|
||
|
||
func formatRangeLine(wf func(string, ...interface{}), group []difflib.OpCode) {
|
||
first, last := group[0], group[len(group)-1]
|
||
range1 := formatRangeUnified(first.I1, last.I2)
|
||
range2 := formatRangeUnified(first.J1, last.J2)
|
||
wf("@@ -%s +%s @@\n", range1, range2)
|
||
}
|
||
|
||
// Convert range to the "ed" format
|
||
func formatRangeUnified(start, stop int) string {
|
||
// Per the diff spec at http://www.unix.org/single_unix_specification/
|
||
beginning := start + 1 // lines start numbering with one
|
||
length := stop - start
|
||
if length == 1 {
|
||
return fmt.Sprintf("%d", beginning)
|
||
}
|
||
if length == 0 {
|
||
beginning-- // empty ranges begin at line just before the range
|
||
}
|
||
return fmt.Sprintf("%d,%d", beginning, length)
|
||
}
|
||
|
||
func formatLines(writeLine func(string, string), prefix string, lines []string) {
|
||
for _, line := range lines {
|
||
writeLine(prefix, line)
|
||
}
|
||
// Add a newline if the last line is missing one so that the diff displays
|
||
// properly.
|
||
if !strings.HasSuffix(lines[len(lines)-1], "\n") {
|
||
writeLine("", "\n")
|
||
}
|
||
}
|