1
0
mirror of https://github.com/golang/go synced 2024-11-05 18:26:10 -07:00
go/internal/lsp/diff/unified.go
Ian Cottrell 744a51dd88 internal/lsp: normalise and make public diff<->edit conversions
This allows us to use the diff.ApplyEdits in tests, saving us from a different
implementation.
It also prepares for command lines that need to use diff features based on the
results of a protocol message.

Splitting content into lines is too easy to get wrong, and needs to be done
correctly or the diff results make no sense. This adds the SplitLines function
to the diff pacakge to do it right and then uses it everwhere we we already
doing it wrong.

It also makes all the diff tests external black box tests.

Change-Id: I698227d5769a2bfbfd22a64ea42906b1df9268d9
Reviewed-on: https://go-review.googlesource.com/c/tools/+/171027
Run-TryBot: Ian Cottrell <iancottrell@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Rebecca Stambler <rstambler@golang.org>
2019-04-15 20:31:36 +00:00

149 lines
2.9 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"
)
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")
}
}
}
}