1
0
mirror of https://github.com/golang/go synced 2024-11-19 06:54:39 -07:00
go/internal/lsp/diff/unified.go
Ian Cottrell cc9bfb3eec internal/lsp: rewrite the unified diff handling
It is now in the diff package written in terms of edits, instead of the myers package.
This also means that the unified handling is no longer pluggable because it does
not need to be.

Change-Id: I7141b023e95ed0c1d21cbc81c7420c117fc5ef1a
Reviewed-on: https://go-review.googlesource.com/c/tools/+/198517
Run-TryBot: Ian Cottrell <iancottrell@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Rebecca Stambler <rstambler@golang.org>
2019-10-04 03:45:17 +00:00

170 lines
3.5 KiB
Go

// 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"
"golang.org/x/tools/internal/span"
)
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, content string, edits []TextEdit) Unified {
u := Unified{
From: from,
To: to,
}
if len(edits) == 0 {
return u
}
lines := splitLines(content)
var h *Hunk
last := 0
c := span.NewContentConverter(from, []byte(content))
toLine := 0
for _, edit := range edits {
spn, _ := edit.Span.WithAll(c)
start := spn.Start().Line() - 1
end := spn.End().Line() - 1
if spn.Start().Column() > 1 || spn.End().Column() > 1 {
panic("cannot convert partial line edits to unified diff")
}
switch {
case start < last:
panic("cannot convert unsorted edits to unified diff")
case h != nil && start == last:
//direct extension
case h != nil && start <= last+gap:
//within range of previous lines, add the joiners
addEqualLines(h, lines, last, start)
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)
}
toLine += start - last
h = &Hunk{
FromLine: start + 1,
ToLine: toLine + 1,
}
// add the edge to the new hunk
delta := addEqualLines(h, lines, start-edge, start)
h.FromLine -= delta
h.ToLine -= delta
}
last = start
if edit.NewText == "" {
for i := start; i < end; i++ {
h.Lines = append(h.Lines, Line{Kind: Delete, Content: lines[i]})
last++
}
} else {
for _, line := range splitLines(edit.NewText) {
h.Lines = append(h.Lines, Line{Kind: Insert, Content: line})
toLine++
}
}
}
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 splitLines(text string) []string {
lines := strings.SplitAfter(text, "\n")
if lines[len(lines)-1] == "" {
lines = lines[:len(lines)-1]
}
return lines
}
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) {
if len(u.Hunks) == 0 {
return
}
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")
}
}
}
}