// Package format provides utilities for formatting diffs and messages.
package format import ( ) 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 ( DiffConfig) string { := strings.SplitAfter(.A, "\n") := strings.SplitAfter(.B, "\n") := difflib.NewMatcher(, ).GetGroupedOpCodes(contextLines) if len() == 0 { return "" } := new(bytes.Buffer) := func( string, ...interface{}) { .WriteString(fmt.Sprintf(, ...)) } := func( string, string) { .WriteString( + ) } if hasWhitespaceDiffLines(, , ) { = visibleWhitespaceLine() } formatHeader(, ) for , := range { formatRangeLine(, ) for , := range { , := [.I1:.I2], [.J1:.J2] switch .Tag { case 'e': formatLines(, " ", ) case 'r': formatLines(, "-", ) formatLines(, "+", ) case 'd': formatLines(, "-", ) case 'i': formatLines(, "+", ) } } } return .String() } // hasWhitespaceDiffLines returns true if any diff groups is only different // because of whitespace characters. func ( [][]difflib.OpCode, , []string) bool { for , := range { , := new(bytes.Buffer), new(bytes.Buffer) for , := range { if .Tag == 'e' { continue } for , := range [.I1:.I2] { .WriteString() } for , := range [.J1:.J2] { .WriteString() } } if removeWhitespace(.String()) == removeWhitespace(.String()) { return true } } return false } func ( string) string { var []rune for , := range { if !unicode.IsSpace() { = append(, ) } } return string() } func ( func(string, string)) func(string, string) { := func( rune) rune { switch { case '\n': case ' ': return '·' case '\t': return '▷' case '\v': return '▽' case '\r': return '↵' case '\f': return '↓' default: if unicode.IsSpace() { return '�' } } return } return func(, string) { (, strings.Map(, )) } } func ( func(string, ...interface{}), DiffConfig) { if .From != "" || .To != "" { ("--- %s\n", .From) ("+++ %s\n", .To) } } func ( func(string, ...interface{}), []difflib.OpCode) { , := [0], [len()-1] := formatRangeUnified(.I1, .I2) := formatRangeUnified(.J1, .J2) ("@@ -%s +%s @@\n", , ) } // Convert range to the "ed" format func (, int) string { // Per the diff spec at http://www.unix.org/single_unix_specification/ := + 1 // lines start numbering with one := - if == 1 { return fmt.Sprintf("%d", ) } if == 0 { -- // empty ranges begin at line just before the range } return fmt.Sprintf("%d,%d", , ) } func ( func(string, string), string, []string) { for , := range { (, ) } // Add a newline if the last line is missing one so that the diff displays // properly. if !strings.HasSuffix([len()-1], "\n") { ("", "\n") } }