blob: 427a8719e43f3e3c38c8b58b5d3a6acf0b8c28ad [file] [log] [blame]
// Copyright 2019 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package diff
import (
"fmt"
"strings"
)
type Unified struct {
From, To string
Hunks []*Hunk
}
type Hunk struct {
FromLine int
ToLine int
Lines []Line
}
type Line struct {
Kind OpKind
Content string
}
const (
edge = 3
gap = edge * 2
)
func ToUnified(from, to string, lines []string, ops []*Op) Unified {
u := Unified{
From: from,
To: to,
}
if len(ops) == 0 {
return u
}
var h *Hunk
last := -(gap + 2)
for _, op := range ops {
switch {
case op.I1 < last:
panic("cannot convert unsorted operations to unified diff")
case op.I1 == last:
//direct extension
case op.I1 <= last+gap:
//within range of previous lines, add the joiners
addEqualLines(h, lines, last, op.I1)
default:
//need to start a new hunk
if h != nil {
// add the edge to the previous hunk
addEqualLines(h, lines, last, last+edge)
u.Hunks = append(u.Hunks, h)
}
h = &Hunk{
FromLine: op.I1 + 1,
ToLine: op.J1 + 1,
}
// add the edge to the new hunk
delta := addEqualLines(h, lines, op.I1-edge, op.I1)
h.FromLine -= delta
h.ToLine -= delta
}
last = op.I1
switch op.Kind {
case Delete:
for i := op.I1; i < op.I2; i++ {
h.Lines = append(h.Lines, Line{Kind: Delete, Content: lines[i]})
last++
}
case Insert:
for _, c := range op.Content {
h.Lines = append(h.Lines, Line{Kind: Insert, Content: c})
}
default:
// all other op types ignored
}
}
if h != nil {
// add the edge to the final hunk
addEqualLines(h, lines, last, last+edge)
u.Hunks = append(u.Hunks, h)
}
return u
}
func addEqualLines(h *Hunk, lines []string, start, end int) int {
delta := 0
for i := start; i < end; i++ {
if i < 0 {
continue
}
if i >= len(lines) {
return delta
}
h.Lines = append(h.Lines, Line{Kind: Equal, Content: lines[i]})
delta++
}
return delta
}
func (u Unified) Format(f fmt.State, r rune) {
fmt.Fprintf(f, "--- %s\n", u.From)
fmt.Fprintf(f, "+++ %s\n", u.To)
for _, hunk := range u.Hunks {
fromCount, toCount := 0, 0
for _, l := range hunk.Lines {
switch l.Kind {
case Delete:
fromCount++
case Insert:
toCount++
default:
fromCount++
toCount++
}
}
fmt.Fprint(f, "@@")
if fromCount > 1 {
fmt.Fprintf(f, " -%d,%d", hunk.FromLine, fromCount)
} else {
fmt.Fprintf(f, " -%d", hunk.FromLine)
}
if toCount > 1 {
fmt.Fprintf(f, " +%d,%d", hunk.ToLine, toCount)
} else {
fmt.Fprintf(f, " +%d", hunk.ToLine)
}
fmt.Fprint(f, " @@\n")
for _, l := range hunk.Lines {
switch l.Kind {
case Delete:
fmt.Fprintf(f, "-%s", l.Content)
case Insert:
fmt.Fprintf(f, "+%s", l.Content)
default:
fmt.Fprintf(f, " %s", l.Content)
}
if !strings.HasSuffix(l.Content, "\n") {
fmt.Fprintf(f, "\n\\ No newline at end of file\n")
}
}
}
}