From c7d7f10bdff703e4a3c0414e8a33d4e45c91eb35 Mon Sep 17 00:00:00 2001 From: Dmitry Vyukov Date: Sat, 4 Jul 2020 11:12:55 +0200 Subject: go.mod: vendor golangci-lint --- vendor/github.com/sourcegraph/go-diff/diff/diff.go | 76 ++ .../github.com/sourcegraph/go-diff/diff/diff.pb.go | 1059 ++++++++++++++++++++ .../github.com/sourcegraph/go-diff/diff/diff.proto | 81 ++ vendor/github.com/sourcegraph/go-diff/diff/doc.go | 2 + .../github.com/sourcegraph/go-diff/diff/parse.go | 625 ++++++++++++ .../github.com/sourcegraph/go-diff/diff/print.go | 140 +++ .../sourcegraph/go-diff/diff/reader_util.go | 37 + 7 files changed, 2020 insertions(+) create mode 100644 vendor/github.com/sourcegraph/go-diff/diff/diff.go create mode 100644 vendor/github.com/sourcegraph/go-diff/diff/diff.pb.go create mode 100644 vendor/github.com/sourcegraph/go-diff/diff/diff.proto create mode 100644 vendor/github.com/sourcegraph/go-diff/diff/doc.go create mode 100644 vendor/github.com/sourcegraph/go-diff/diff/parse.go create mode 100644 vendor/github.com/sourcegraph/go-diff/diff/print.go create mode 100644 vendor/github.com/sourcegraph/go-diff/diff/reader_util.go (limited to 'vendor/github.com/sourcegraph/go-diff/diff') diff --git a/vendor/github.com/sourcegraph/go-diff/diff/diff.go b/vendor/github.com/sourcegraph/go-diff/diff/diff.go new file mode 100644 index 000000000..646602a6c --- /dev/null +++ b/vendor/github.com/sourcegraph/go-diff/diff/diff.go @@ -0,0 +1,76 @@ +package diff + +import "bytes" + +// NOTE: types are code-generated in diff.pb.go. + +//go:generate protoc -I../../../.. -I ../../../../github.com/gogo/protobuf/protobuf -I. --gogo_out=. diff.proto + +// Stat computes the number of lines added/changed/deleted in all +// hunks in this file's diff. +func (d *FileDiff) Stat() Stat { + total := Stat{} + for _, h := range d.Hunks { + total.add(h.Stat()) + } + return total +} + +// Stat computes the number of lines added/changed/deleted in this +// hunk. +func (h *Hunk) Stat() Stat { + lines := bytes.Split(h.Body, []byte{'\n'}) + var last byte + st := Stat{} + for _, line := range lines { + if len(line) == 0 { + last = 0 + continue + } + switch line[0] { + case '-': + if last == '+' { + st.Added-- + st.Changed++ + last = 0 // next line can't change this one since this is already a change + } else { + st.Deleted++ + last = line[0] + } + case '+': + if last == '-' { + st.Deleted-- + st.Changed++ + last = 0 // next line can't change this one since this is already a change + } else { + st.Added++ + last = line[0] + } + default: + last = 0 + } + } + return st +} + +var ( + hunkPrefix = []byte("@@ ") +) + +const hunkHeader = "@@ -%d,%d +%d,%d @@" + +// diffTimeParseLayout is the layout used to parse the time in unified diff file +// header timestamps. +// See https://www.gnu.org/software/diffutils/manual/html_node/Detailed-Unified.html. +const diffTimeParseLayout = "2006-01-02 15:04:05 -0700" + +// diffTimeFormatLayout is the layout used to format (i.e., print) the time in unified diff file +// header timestamps. +// See https://www.gnu.org/software/diffutils/manual/html_node/Detailed-Unified.html. +const diffTimeFormatLayout = "2006-01-02 15:04:05.000000000 -0700" + +func (s *Stat) add(o Stat) { + s.Added += o.Added + s.Changed += o.Changed + s.Deleted += o.Deleted +} diff --git a/vendor/github.com/sourcegraph/go-diff/diff/diff.pb.go b/vendor/github.com/sourcegraph/go-diff/diff/diff.pb.go new file mode 100644 index 000000000..2e7c27fb4 --- /dev/null +++ b/vendor/github.com/sourcegraph/go-diff/diff/diff.pb.go @@ -0,0 +1,1059 @@ +// Code generated by protoc-gen-gogo. +// source: diff.proto +// DO NOT EDIT! + +/* + Package diff is a generated protocol buffer package. + + It is generated from these files: + diff.proto + + It has these top-level messages: + FileDiff + Hunk + Stat +*/ +package diff + +import proto "github.com/gogo/protobuf/proto" +import fmt "fmt" +import math "math" + +// discarding unused import gogoproto "github.com/gogo/protobuf/gogoproto" +import pbtypes "sourcegraph.com/sqs/pbtypes" + +import io "io" + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// A FileDiff represents a unified diff for a single file. +// +// A file unified diff has a header that resembles the following: +// +// --- oldname 2009-10-11 15:12:20.000000000 -0700 +// +++ newname 2009-10-11 15:12:30.000000000 -0700 +type FileDiff struct { + // the original name of the file + OrigName string `protobuf:"bytes,1,opt,name=OrigName,proto3" json:"OrigName,omitempty"` + // the original timestamp (nil if not present) + OrigTime *pbtypes.Timestamp `protobuf:"bytes,2,opt,name=OrigTime" json:"OrigTime,omitempty"` + // the new name of the file (often same as OrigName) + NewName string `protobuf:"bytes,3,opt,name=NewName,proto3" json:"NewName,omitempty"` + // the new timestamp (nil if not present) + NewTime *pbtypes.Timestamp `protobuf:"bytes,4,opt,name=NewTime" json:"NewTime,omitempty"` + // extended header lines (e.g., git's "new mode ", "rename from ", etc.) + Extended []string `protobuf:"bytes,5,rep,name=Extended" json:"Extended,omitempty"` + // hunks that were changed from orig to new + Hunks []*Hunk `protobuf:"bytes,6,rep,name=Hunks" json:"Hunks,omitempty"` +} + +func (m *FileDiff) Reset() { *m = FileDiff{} } +func (m *FileDiff) String() string { return proto.CompactTextString(m) } +func (*FileDiff) ProtoMessage() {} + +// A Hunk represents a series of changes (additions or deletions) in a file's +// unified diff. +type Hunk struct { + // starting line number in original file + OrigStartLine int32 `protobuf:"varint,1,opt,name=OrigStartLine,proto3" json:"OrigStartLine,omitempty"` + // number of lines the hunk applies to in the original file + OrigLines int32 `protobuf:"varint,2,opt,name=OrigLines,proto3" json:"OrigLines,omitempty"` + // if > 0, then the original file had a 'No newline at end of file' mark at this offset + OrigNoNewlineAt int32 `protobuf:"varint,3,opt,name=OrigNoNewlineAt,proto3" json:"OrigNoNewlineAt,omitempty"` + // starting line number in new file + NewStartLine int32 `protobuf:"varint,4,opt,name=NewStartLine,proto3" json:"NewStartLine,omitempty"` + // number of lines the hunk applies to in the new file + NewLines int32 `protobuf:"varint,5,opt,name=NewLines,proto3" json:"NewLines,omitempty"` + // optional section heading + Section string `protobuf:"bytes,6,opt,name=Section,proto3" json:"Section,omitempty"` + // 0-indexed line offset in unified file diff (including section headers); this is + // only set when Hunks are read from entire file diff (i.e., when ReadAllHunks is + // called) This accounts for hunk headers, too, so the StartPosition of the first + // hunk will be 1. + StartPosition int32 `protobuf:"varint,7,opt,name=StartPosition,proto3" json:"StartPosition,omitempty"` + // hunk body (lines prefixed with '-', '+', or ' ') + Body []byte `protobuf:"bytes,8,opt,name=Body,proto3" json:"Body,omitempty"` +} + +func (m *Hunk) Reset() { *m = Hunk{} } +func (m *Hunk) String() string { return proto.CompactTextString(m) } +func (*Hunk) ProtoMessage() {} + +// A Stat is a diff stat that represents the number of lines added/changed/deleted. +type Stat struct { + // number of lines added + Added int32 `protobuf:"varint,1,opt,name=Added,proto3" json:""` + // number of lines changed + Changed int32 `protobuf:"varint,2,opt,name=Changed,proto3" json:""` + // number of lines deleted + Deleted int32 `protobuf:"varint,3,opt,name=Deleted,proto3" json:""` +} + +func (m *Stat) Reset() { *m = Stat{} } +func (m *Stat) String() string { return proto.CompactTextString(m) } +func (*Stat) ProtoMessage() {} + +func (m *FileDiff) Marshal() (data []byte, err error) { + size := m.Size() + data = make([]byte, size) + n, err := m.MarshalTo(data) + if err != nil { + return nil, err + } + return data[:n], nil +} + +func (m *FileDiff) MarshalTo(data []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if len(m.OrigName) > 0 { + data[i] = 0xa + i++ + i = encodeVarintDiff(data, i, uint64(len(m.OrigName))) + i += copy(data[i:], m.OrigName) + } + if m.OrigTime != nil { + data[i] = 0x12 + i++ + i = encodeVarintDiff(data, i, uint64(m.OrigTime.Size())) + n1, err := m.OrigTime.MarshalTo(data[i:]) + if err != nil { + return 0, err + } + i += n1 + } + if len(m.NewName) > 0 { + data[i] = 0x1a + i++ + i = encodeVarintDiff(data, i, uint64(len(m.NewName))) + i += copy(data[i:], m.NewName) + } + if m.NewTime != nil { + data[i] = 0x22 + i++ + i = encodeVarintDiff(data, i, uint64(m.NewTime.Size())) + n2, err := m.NewTime.MarshalTo(data[i:]) + if err != nil { + return 0, err + } + i += n2 + } + if len(m.Extended) > 0 { + for _, s := range m.Extended { + data[i] = 0x2a + i++ + l = len(s) + for l >= 1<<7 { + data[i] = uint8(uint64(l)&0x7f | 0x80) + l >>= 7 + i++ + } + data[i] = uint8(l) + i++ + i += copy(data[i:], s) + } + } + if len(m.Hunks) > 0 { + for _, msg := range m.Hunks { + data[i] = 0x32 + i++ + i = encodeVarintDiff(data, i, uint64(msg.Size())) + n, err := msg.MarshalTo(data[i:]) + if err != nil { + return 0, err + } + i += n + } + } + return i, nil +} + +func (m *Hunk) Marshal() (data []byte, err error) { + size := m.Size() + data = make([]byte, size) + n, err := m.MarshalTo(data) + if err != nil { + return nil, err + } + return data[:n], nil +} + +func (m *Hunk) MarshalTo(data []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if m.OrigStartLine != 0 { + data[i] = 0x8 + i++ + i = encodeVarintDiff(data, i, uint64(m.OrigStartLine)) + } + if m.OrigLines != 0 { + data[i] = 0x10 + i++ + i = encodeVarintDiff(data, i, uint64(m.OrigLines)) + } + if m.OrigNoNewlineAt != 0 { + data[i] = 0x18 + i++ + i = encodeVarintDiff(data, i, uint64(m.OrigNoNewlineAt)) + } + if m.NewStartLine != 0 { + data[i] = 0x20 + i++ + i = encodeVarintDiff(data, i, uint64(m.NewStartLine)) + } + if m.NewLines != 0 { + data[i] = 0x28 + i++ + i = encodeVarintDiff(data, i, uint64(m.NewLines)) + } + if len(m.Section) > 0 { + data[i] = 0x32 + i++ + i = encodeVarintDiff(data, i, uint64(len(m.Section))) + i += copy(data[i:], m.Section) + } + if m.StartPosition != 0 { + data[i] = 0x38 + i++ + i = encodeVarintDiff(data, i, uint64(m.StartPosition)) + } + if m.Body != nil { + if len(m.Body) > 0 { + data[i] = 0x42 + i++ + i = encodeVarintDiff(data, i, uint64(len(m.Body))) + i += copy(data[i:], m.Body) + } + } + return i, nil +} + +func (m *Stat) Marshal() (data []byte, err error) { + size := m.Size() + data = make([]byte, size) + n, err := m.MarshalTo(data) + if err != nil { + return nil, err + } + return data[:n], nil +} + +func (m *Stat) MarshalTo(data []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if m.Added != 0 { + data[i] = 0x8 + i++ + i = encodeVarintDiff(data, i, uint64(m.Added)) + } + if m.Changed != 0 { + data[i] = 0x10 + i++ + i = encodeVarintDiff(data, i, uint64(m.Changed)) + } + if m.Deleted != 0 { + data[i] = 0x18 + i++ + i = encodeVarintDiff(data, i, uint64(m.Deleted)) + } + return i, nil +} + +func encodeFixed64Diff(data []byte, offset int, v uint64) int { + data[offset] = uint8(v) + data[offset+1] = uint8(v >> 8) + data[offset+2] = uint8(v >> 16) + data[offset+3] = uint8(v >> 24) + data[offset+4] = uint8(v >> 32) + data[offset+5] = uint8(v >> 40) + data[offset+6] = uint8(v >> 48) + data[offset+7] = uint8(v >> 56) + return offset + 8 +} +func encodeFixed32Diff(data []byte, offset int, v uint32) int { + data[offset] = uint8(v) + data[offset+1] = uint8(v >> 8) + data[offset+2] = uint8(v >> 16) + data[offset+3] = uint8(v >> 24) + return offset + 4 +} +func encodeVarintDiff(data []byte, offset int, v uint64) int { + for v >= 1<<7 { + data[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + data[offset] = uint8(v) + return offset + 1 +} +func (m *FileDiff) Size() (n int) { + var l int + _ = l + l = len(m.OrigName) + if l > 0 { + n += 1 + l + sovDiff(uint64(l)) + } + if m.OrigTime != nil { + l = m.OrigTime.Size() + n += 1 + l + sovDiff(uint64(l)) + } + l = len(m.NewName) + if l > 0 { + n += 1 + l + sovDiff(uint64(l)) + } + if m.NewTime != nil { + l = m.NewTime.Size() + n += 1 + l + sovDiff(uint64(l)) + } + if len(m.Extended) > 0 { + for _, s := range m.Extended { + l = len(s) + n += 1 + l + sovDiff(uint64(l)) + } + } + if len(m.Hunks) > 0 { + for _, e := range m.Hunks { + l = e.Size() + n += 1 + l + sovDiff(uint64(l)) + } + } + return n +} + +func (m *Hunk) Size() (n int) { + var l int + _ = l + if m.OrigStartLine != 0 { + n += 1 + sovDiff(uint64(m.OrigStartLine)) + } + if m.OrigLines != 0 { + n += 1 + sovDiff(uint64(m.OrigLines)) + } + if m.OrigNoNewlineAt != 0 { + n += 1 + sovDiff(uint64(m.OrigNoNewlineAt)) + } + if m.NewStartLine != 0 { + n += 1 + sovDiff(uint64(m.NewStartLine)) + } + if m.NewLines != 0 { + n += 1 + sovDiff(uint64(m.NewLines)) + } + l = len(m.Section) + if l > 0 { + n += 1 + l + sovDiff(uint64(l)) + } + if m.StartPosition != 0 { + n += 1 + sovDiff(uint64(m.StartPosition)) + } + if m.Body != nil { + l = len(m.Body) + if l > 0 { + n += 1 + l + sovDiff(uint64(l)) + } + } + return n +} + +func (m *Stat) Size() (n int) { + var l int + _ = l + if m.Added != 0 { + n += 1 + sovDiff(uint64(m.Added)) + } + if m.Changed != 0 { + n += 1 + sovDiff(uint64(m.Changed)) + } + if m.Deleted != 0 { + n += 1 + sovDiff(uint64(m.Deleted)) + } + return n +} + +func sovDiff(x uint64) (n int) { + for { + n++ + x >>= 7 + if x == 0 { + break + } + } + return n +} +func sozDiff(x uint64) (n int) { + return sovDiff(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *FileDiff) Unmarshal(data []byte) error { + l := len(data) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDiff + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := data[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: FileDiff: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: FileDiff: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field OrigName", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDiff + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := data[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthDiff + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.OrigName = string(data[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field OrigTime", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDiff + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := data[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthDiff + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.OrigTime == nil { + m.OrigTime = &pbtypes.Timestamp{} + } + if err := m.OrigTime.Unmarshal(data[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field NewName", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDiff + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := data[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthDiff + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.NewName = string(data[iNdEx:postIndex]) + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field NewTime", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDiff + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := data[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthDiff + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.NewTime == nil { + m.NewTime = &pbtypes.Timestamp{} + } + if err := m.NewTime.Unmarshal(data[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Extended", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDiff + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := data[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthDiff + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Extended = append(m.Extended, string(data[iNdEx:postIndex])) + iNdEx = postIndex + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Hunks", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDiff + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := data[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthDiff + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Hunks = append(m.Hunks, &Hunk{}) + if err := m.Hunks[len(m.Hunks)-1].Unmarshal(data[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipDiff(data[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthDiff + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Hunk) Unmarshal(data []byte) error { + l := len(data) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDiff + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := data[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Hunk: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Hunk: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field OrigStartLine", wireType) + } + m.OrigStartLine = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDiff + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := data[iNdEx] + iNdEx++ + m.OrigStartLine |= (int32(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field OrigLines", wireType) + } + m.OrigLines = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDiff + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := data[iNdEx] + iNdEx++ + m.OrigLines |= (int32(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field OrigNoNewlineAt", wireType) + } + m.OrigNoNewlineAt = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDiff + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := data[iNdEx] + iNdEx++ + m.OrigNoNewlineAt |= (int32(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field NewStartLine", wireType) + } + m.NewStartLine = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDiff + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := data[iNdEx] + iNdEx++ + m.NewStartLine |= (int32(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + case 5: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field NewLines", wireType) + } + m.NewLines = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDiff + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := data[iNdEx] + iNdEx++ + m.NewLines |= (int32(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Section", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDiff + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := data[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthDiff + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Section = string(data[iNdEx:postIndex]) + iNdEx = postIndex + case 7: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field StartPosition", wireType) + } + m.StartPosition = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDiff + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := data[iNdEx] + iNdEx++ + m.StartPosition |= (int32(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + case 8: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Body", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDiff + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := data[iNdEx] + iNdEx++ + byteLen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthDiff + } + postIndex := iNdEx + byteLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Body = append([]byte{}, data[iNdEx:postIndex]...) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipDiff(data[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthDiff + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Stat) Unmarshal(data []byte) error { + l := len(data) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDiff + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := data[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Stat: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Stat: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Added", wireType) + } + m.Added = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDiff + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := data[iNdEx] + iNdEx++ + m.Added |= (int32(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Changed", wireType) + } + m.Changed = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDiff + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := data[iNdEx] + iNdEx++ + m.Changed |= (int32(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Deleted", wireType) + } + m.Deleted = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDiff + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := data[iNdEx] + iNdEx++ + m.Deleted |= (int32(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipDiff(data[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthDiff + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipDiff(data []byte) (n int, err error) { + l := len(data) + iNdEx := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowDiff + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := data[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowDiff + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if data[iNdEx-1] < 0x80 { + break + } + } + return iNdEx, nil + case 1: + iNdEx += 8 + return iNdEx, nil + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowDiff + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := data[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + iNdEx += length + if length < 0 { + return 0, ErrInvalidLengthDiff + } + return iNdEx, nil + case 3: + for { + var innerWire uint64 + var start int = iNdEx + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowDiff + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := data[iNdEx] + iNdEx++ + innerWire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + innerWireType := int(innerWire & 0x7) + if innerWireType == 4 { + break + } + next, err := skipDiff(data[start:]) + if err != nil { + return 0, err + } + iNdEx = start + next + } + return iNdEx, nil + case 4: + return iNdEx, nil + case 5: + iNdEx += 4 + return iNdEx, nil + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + } + panic("unreachable") +} + +var ( + ErrInvalidLengthDiff = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowDiff = fmt.Errorf("proto: integer overflow") +) diff --git a/vendor/github.com/sourcegraph/go-diff/diff/diff.proto b/vendor/github.com/sourcegraph/go-diff/diff/diff.proto new file mode 100644 index 000000000..8868970f6 --- /dev/null +++ b/vendor/github.com/sourcegraph/go-diff/diff/diff.proto @@ -0,0 +1,81 @@ +syntax = "proto3"; +package diff; + +import "github.com/gogo/protobuf/gogoproto/gogo.proto"; +import "sourcegraph.com/sqs/pbtypes/timestamp.proto"; + +option (gogoproto.goproto_getters_all) = false; +option (gogoproto.unmarshaler_all) = true; +option (gogoproto.marshaler_all) = true; +option (gogoproto.sizer_all) = true; + +// A FileDiff represents a unified diff for a single file. +// +// A file unified diff has a header that resembles the following: +// +// --- oldname 2009-10-11 15:12:20.000000000 -0700 +// +++ newname 2009-10-11 15:12:30.000000000 -0700 +message FileDiff { + // the original name of the file + string OrigName = 1; + + // the original timestamp (nil if not present) + pbtypes.Timestamp OrigTime = 2; + + // the new name of the file (often same as OrigName) + string NewName = 3; + + // the new timestamp (nil if not present) + pbtypes.Timestamp NewTime = 4; + + // extended header lines (e.g., git's "new mode ", "rename from ", etc.) + repeated string Extended = 5; + + // hunks that were changed from orig to new + repeated Hunk Hunks = 6; +} + + +// A Hunk represents a series of changes (additions or deletions) in a file's +// unified diff. +message Hunk { + // starting line number in original file + int32 OrigStartLine = 1; + + // number of lines the hunk applies to in the original file + int32 OrigLines = 2; + + // if > 0, then the original file had a 'No newline at end of file' mark at this offset + int32 OrigNoNewlineAt = 3; + + // starting line number in new file + int32 NewStartLine = 4; + + // number of lines the hunk applies to in the new file + int32 NewLines = 5; + + // optional section heading + string Section = 6; + + // 0-indexed line offset in unified file diff (including section headers); this is + // only set when Hunks are read from entire file diff (i.e., when ReadAllHunks is + // called) This accounts for hunk headers, too, so the StartPosition of the first + // hunk will be 1. + int32 StartPosition = 7; + + // hunk body (lines prefixed with '-', '+', or ' ') + bytes Body = 8; +} + +// A Stat is a diff stat that represents the number of lines added/changed/deleted. +message Stat { + // number of lines added + int32 Added = 1 [(gogoproto.jsontag) = ""]; + + // number of lines changed + int32 Changed = 2 [(gogoproto.jsontag) = ""]; + + // number of lines deleted + int32 Deleted = 3 [(gogoproto.jsontag) = ""]; +} + diff --git a/vendor/github.com/sourcegraph/go-diff/diff/doc.go b/vendor/github.com/sourcegraph/go-diff/diff/doc.go new file mode 100644 index 000000000..12fe96a07 --- /dev/null +++ b/vendor/github.com/sourcegraph/go-diff/diff/doc.go @@ -0,0 +1,2 @@ +// Package diff provides a parser for unified diffs. +package diff diff --git a/vendor/github.com/sourcegraph/go-diff/diff/parse.go b/vendor/github.com/sourcegraph/go-diff/diff/parse.go new file mode 100644 index 000000000..1e77df48f --- /dev/null +++ b/vendor/github.com/sourcegraph/go-diff/diff/parse.go @@ -0,0 +1,625 @@ +package diff + +import ( + "bufio" + "bytes" + "errors" + "fmt" + "io" + "strings" + "time" + + "sourcegraph.com/sqs/pbtypes" +) + +// ParseMultiFileDiff parses a multi-file unified diff. It returns an error if parsing failed as a whole, but does its +// best to parse as many files in the case of per-file errors. In the case of non-fatal per-file errors, the error +// return value is null and the Errs field in the returned MultiFileDiff is set. +func ParseMultiFileDiff(diff []byte) ([]*FileDiff, error) { + return NewMultiFileDiffReader(bytes.NewReader(diff)).ReadAllFiles() +} + +// NewMultiFileDiffReader returns a new MultiFileDiffReader that reads +// a multi-file unified diff from r. +func NewMultiFileDiffReader(r io.Reader) *MultiFileDiffReader { + return &MultiFileDiffReader{reader: bufio.NewReader(r)} +} + +// MultiFileDiffReader reads a multi-file unified diff. +type MultiFileDiffReader struct { + line int + offset int64 + reader *bufio.Reader + + // TODO(sqs): line and offset tracking in multi-file diffs is broken; add tests and fix + + // nextFileFirstLine is a line that was read by a HunksReader that + // was how it determined the hunk was complete. But to determine + // that, it needed to read the first line of the next file. We + // store nextFileFirstLine so we can "give the first line back" to + // the next file. + nextFileFirstLine []byte +} + +// ReadFile reads the next file unified diff (including headers and +// all hunks) from r. If there are no more files in the diff, it +// returns error io.EOF. +func (r *MultiFileDiffReader) ReadFile() (*FileDiff, error) { + fr := &FileDiffReader{ + line: r.line, + offset: r.offset, + reader: r.reader, + fileHeaderLine: r.nextFileFirstLine, + } + r.nextFileFirstLine = nil + + fd, err := fr.ReadAllHeaders() + if err != nil { + switch e := err.(type) { + case *ParseError: + if e.Err == ErrNoFileHeader || e.Err == ErrExtendedHeadersEOF { + return nil, io.EOF + } + + case OverflowError: + r.nextFileFirstLine = []byte(e) + return fd, nil + + default: + return nil, err + } + } + + // Before reading hunks, check to see if there are any. If there + // aren't any, and there's another file after this file in the + // diff, then the hunks reader will complain ErrNoHunkHeader. It's + // not easy for us to tell from that error alone if that was + // caused by the lack of any hunks, or a malformatted hunk, so we + // need to perform the check here. + hr := fr.HunksReader() + line, err := readLine(r.reader) + if err != nil { + return fd, err + } + line = bytes.TrimSuffix(line, []byte{'\n'}) + if bytes.HasPrefix(line, hunkPrefix) { + hr.nextHunkHeaderLine = line + fd.Hunks, err = hr.ReadAllHunks() + r.line = fr.line + r.offset = fr.offset + if err != nil { + if e0, ok := err.(*ParseError); ok { + if e, ok := e0.Err.(*ErrBadHunkLine); ok { + // This just means we finished reading the hunks for the + // current file. See the ErrBadHunkLine doc for more info. + r.nextFileFirstLine = e.Line + return fd, nil + } + } + return nil, err + } + } else { + // There weren't any hunks, so that line we peeked ahead at + // actually belongs to the next file. Put it back. + r.nextFileFirstLine = line + } + + return fd, nil +} + +// ReadAllFiles reads all file unified diffs (including headers and all +// hunks) remaining in r. +func (r *MultiFileDiffReader) ReadAllFiles() ([]*FileDiff, error) { + var ds []*FileDiff + for { + d, err := r.ReadFile() + if d != nil { + ds = append(ds, d) + } + if err == io.EOF { + return ds, nil + } + if err != nil { + return nil, err + } + } +} + +// ParseFileDiff parses a file unified diff. +func ParseFileDiff(diff []byte) (*FileDiff, error) { + return NewFileDiffReader(bytes.NewReader(diff)).Read() +} + +// NewFileDiffReader returns a new FileDiffReader that reads a file +// unified diff. +func NewFileDiffReader(r io.Reader) *FileDiffReader { + return &FileDiffReader{reader: bufio.NewReader(r)} +} + +// FileDiffReader reads a unified file diff. +type FileDiffReader struct { + line int + offset int64 + reader *bufio.Reader + + // fileHeaderLine is the first file header line, set by: + // + // (1) ReadExtendedHeaders if it encroaches on a file header line + // (which it must to detect when extended headers are done); or + // (2) (*MultiFileDiffReader).ReadFile() if it encroaches on a + // file header line while reading the previous file's hunks (in a + // multi-file diff). + fileHeaderLine []byte +} + +// Read reads a file unified diff, including headers and hunks, from r. +func (r *FileDiffReader) Read() (*FileDiff, error) { + fd, err := r.ReadAllHeaders() + if err != nil { + return nil, err + } + + fd.Hunks, err = r.HunksReader().ReadAllHunks() + if err != nil { + return nil, err + } + + return fd, nil +} + +// ReadAllHeaders reads the file headers and extended headers (if any) +// from a file unified diff. It does not read hunks, and the returned +// FileDiff's Hunks field is nil. To read the hunks, call the +// (*FileDiffReader).HunksReader() method to get a HunksReader and +// read hunks from that. +func (r *FileDiffReader) ReadAllHeaders() (*FileDiff, error) { + var err error + fd := &FileDiff{} + + fd.Extended, err = r.ReadExtendedHeaders() + if pe, ok := err.(*ParseError); ok && pe.Err == ErrExtendedHeadersEOF { + wasEmpty := handleEmpty(fd) + if wasEmpty { + return fd, nil + } + return fd, err + } else if _, ok := err.(OverflowError); ok { + handleEmpty(fd) + return fd, err + } else if err != nil { + return fd, err + } + + var origTime, newTime *time.Time + fd.OrigName, fd.NewName, origTime, newTime, err = r.ReadFileHeaders() + if err != nil { + return nil, err + } + if origTime != nil { + ts := pbtypes.NewTimestamp(*origTime) + fd.OrigTime = &ts + } + if newTime != nil { + ts := pbtypes.NewTimestamp(*newTime) + fd.NewTime = &ts + } + + return fd, nil +} + +// HunksReader returns a new HunksReader that reads hunks from r. The +// HunksReader's line and offset (used in error messages) is set to +// start where the file diff header ended (which means errors have the +// correct position information). +func (r *FileDiffReader) HunksReader() *HunksReader { + return &HunksReader{ + line: r.line, + offset: r.offset, + reader: r.reader, + } +} + +// ReadFileHeaders reads the unified file diff header (the lines that +// start with "---" and "+++" with the orig/new file names and +// timestamps). +func (r *FileDiffReader) ReadFileHeaders() (origName, newName string, origTimestamp, newTimestamp *time.Time, err error) { + origName, origTimestamp, err = r.readOneFileHeader([]byte("--- ")) + if err != nil { + return "", "", nil, nil, err + } + + newName, newTimestamp, err = r.readOneFileHeader([]byte("+++ ")) + if err != nil { + return "", "", nil, nil, err + } + + return origName, newName, origTimestamp, newTimestamp, nil +} + +// readOneFileHeader reads one of the file headers (prefix should be +// either "+++ " or "--- "). +func (r *FileDiffReader) readOneFileHeader(prefix []byte) (filename string, timestamp *time.Time, err error) { + var line []byte + + if r.fileHeaderLine == nil { + var err error + line, err = readLine(r.reader) + if err == io.EOF { + return "", nil, &ParseError{r.line, r.offset, ErrNoFileHeader} + } else if err != nil { + return "", nil, err + } + } else { + line = r.fileHeaderLine + r.fileHeaderLine = nil + } + + if !bytes.HasPrefix(line, prefix) { + return "", nil, &ParseError{r.line, r.offset, ErrBadFileHeader} + } + + r.offset += int64(len(line)) + r.line++ + line = line[len(prefix):] + + trimmedLine := strings.TrimSpace(string(line)) // filenames that contain spaces may be terminated by a tab + parts := strings.SplitN(trimmedLine, "\t", 2) + filename = parts[0] + if len(parts) == 2 { + // Timestamp is optional, but this header has it. + ts, err := time.Parse(diffTimeParseLayout, parts[1]) + if err != nil { + return "", nil, err + } + timestamp = &ts + } + + return filename, timestamp, err +} + +// OverflowError is returned when we have overflowed into the start +// of the next file while reading extended headers. +type OverflowError string + +func (e OverflowError) Error() string { + return fmt.Sprintf("overflowed into next file: %s", e) +} + +// ReadExtendedHeaders reads the extended header lines, if any, from a +// unified diff file (e.g., git's "diff --git a/foo.go b/foo.go", "new +// mode ", "rename from ", etc.). +func (r *FileDiffReader) ReadExtendedHeaders() ([]string, error) { + var xheaders []string + firstLine := true + for { + var line []byte + if r.fileHeaderLine == nil { + var err error + line, err = readLine(r.reader) + if err == io.EOF { + return xheaders, &ParseError{r.line, r.offset, ErrExtendedHeadersEOF} + } else if err != nil { + return xheaders, err + } + } else { + line = r.fileHeaderLine + r.fileHeaderLine = nil + } + + if bytes.HasPrefix(line, []byte("diff --git ")) { + if firstLine { + firstLine = false + } else { + return xheaders, OverflowError(line) + } + } + if bytes.HasPrefix(line, []byte("--- ")) { + // We've reached the file header. + r.fileHeaderLine = line // pass to readOneFileHeader (see fileHeaderLine field doc) + return xheaders, nil + } + + r.line++ + r.offset += int64(len(line)) + xheaders = append(xheaders, string(line)) + } +} + +// handleEmpty detects when FileDiff was an empty diff and will not have any hunks +// that follow. It updates fd fields from the parsed extended headers. +func handleEmpty(fd *FileDiff) (wasEmpty bool) { + switch { + case (len(fd.Extended) == 3 || len(fd.Extended) == 4 && strings.HasPrefix(fd.Extended[3], "Binary files ")) && + strings.HasPrefix(fd.Extended[1], "new file mode ") && strings.HasPrefix(fd.Extended[0], "diff --git "): + + names := strings.SplitN(fd.Extended[0][len("diff --git "):], " ", 2) + fd.OrigName = "/dev/null" + fd.NewName = names[1] + return true + case (len(fd.Extended) == 3 || len(fd.Extended) == 4 && strings.HasPrefix(fd.Extended[3], "Binary files ")) && + strings.HasPrefix(fd.Extended[1], "deleted file mode ") && strings.HasPrefix(fd.Extended[0], "diff --git "): + + names := strings.SplitN(fd.Extended[0][len("diff --git "):], " ", 2) + fd.OrigName = names[0] + fd.NewName = "/dev/null" + return true + case len(fd.Extended) == 4 && strings.HasPrefix(fd.Extended[2], "rename from ") && strings.HasPrefix(fd.Extended[3], "rename to ") && strings.HasPrefix(fd.Extended[0], "diff --git "): + names := strings.SplitN(fd.Extended[0][len("diff --git "):], " ", 2) + fd.OrigName = names[0] + fd.NewName = names[1] + return true + case len(fd.Extended) == 3 && strings.HasPrefix(fd.Extended[2], "Binary files ") && strings.HasPrefix(fd.Extended[0], "diff --git "): + names := strings.SplitN(fd.Extended[0][len("diff --git "):], " ", 2) + fd.OrigName = names[0] + fd.NewName = names[1] + return true + default: + return false + } +} + +var ( + // ErrNoFileHeader is when a file unified diff has no file header + // (i.e., the lines that begin with "---" and "+++"). + ErrNoFileHeader = errors.New("expected file header, got EOF") + + // ErrBadFileHeader is when a file unified diff has a malformed + // file header (i.e., the lines that begin with "---" and "+++"). + ErrBadFileHeader = errors.New("bad file header") + + // ErrExtendedHeadersEOF is when an EOF was encountered while reading extended file headers, which means that there were no ---/+++ headers encountered before hunks (if any) began. + ErrExtendedHeadersEOF = errors.New("expected file header while reading extended headers, got EOF") +) + +// ParseHunks parses hunks from a unified diff. The diff must consist +// only of hunks and not include a file header; if it has a file +// header, use ParseFileDiff. +func ParseHunks(diff []byte) ([]*Hunk, error) { + r := NewHunksReader(bytes.NewReader(diff)) + hunks, err := r.ReadAllHunks() + if err != nil { + return nil, err + } + return hunks, nil +} + +// NewHunksReader returns a new HunksReader that reads unified diff hunks +// from r. +func NewHunksReader(r io.Reader) *HunksReader { + return &HunksReader{reader: bufio.NewReader(r)} +} + +// A HunksReader reads hunks from a unified diff. +type HunksReader struct { + line int + offset int64 + hunk *Hunk + reader *bufio.Reader + + nextHunkHeaderLine []byte +} + +// ReadHunk reads one hunk from r. If there are no more hunks, it +// returns error io.EOF. +func (r *HunksReader) ReadHunk() (*Hunk, error) { + r.hunk = nil + lastLineFromOrig := true + var line []byte + var err error + for { + if r.nextHunkHeaderLine != nil { + // Use stored hunk header line that was scanned in at the + // completion of the previous hunk's ReadHunk. + line = r.nextHunkHeaderLine + r.nextHunkHeaderLine = nil + } else { + line, err = readLine(r.reader) + if err != nil { + if err == io.EOF && r.hunk != nil { + return r.hunk, nil + } + return nil, err + } + } + + // Record position. + r.line++ + r.offset += int64(len(line)) + + if r.hunk == nil { + // Check for presence of hunk header. + if !bytes.HasPrefix(line, hunkPrefix) { + return nil, &ParseError{r.line, r.offset, ErrNoHunkHeader} + } + + // Parse hunk header. + r.hunk = &Hunk{} + items := []interface{}{ + &r.hunk.OrigStartLine, &r.hunk.OrigLines, + &r.hunk.NewStartLine, &r.hunk.NewLines, + } + header, section, err := normalizeHeader(string(line)) + if err != nil { + return nil, &ParseError{r.line, r.offset, err} + } + n, err := fmt.Sscanf(header, hunkHeader, items...) + if err != nil { + return nil, err + } + if n < len(items) { + return nil, &ParseError{r.line, r.offset, &ErrBadHunkHeader{header: string(line)}} + } + + r.hunk.Section = section + } else { + // Read hunk body line. + if bytes.HasPrefix(line, hunkPrefix) { + // Saw start of new hunk, so this hunk is + // complete. But we've already read in the next hunk's + // header, so we need to be sure that the next call to + // ReadHunk starts with that header. + r.nextHunkHeaderLine = line + + // Rewind position. + r.line-- + r.offset -= int64(len(line)) + + return r.hunk, nil + } + + if len(line) >= 1 && !linePrefix(line[0]) { + // Bad hunk header line. If we're reading a multi-file + // diff, this may be the end of the current + // file. Return a "rich" error that lets our caller + // handle that case. + return r.hunk, &ParseError{r.line, r.offset, &ErrBadHunkLine{Line: line}} + } + if bytes.Equal(line, []byte(noNewlineMessage)) { + if lastLineFromOrig { + // Retain the newline in the body (otherwise the + // diff line would be like "-a+b", where "+b" is + // the the next line of the new file, which is not + // validly formatted) but record that the orig had + // no newline. + r.hunk.OrigNoNewlineAt = int32(len(r.hunk.Body)) + } else { + // Remove previous line's newline. + if len(r.hunk.Body) != 0 { + r.hunk.Body = r.hunk.Body[:len(r.hunk.Body)-1] + } + } + continue + } + + if len(line) > 0 { + lastLineFromOrig = line[0] == '-' + } + + r.hunk.Body = append(r.hunk.Body, line...) + r.hunk.Body = append(r.hunk.Body, '\n') + } + } +} + +const noNewlineMessage = `\ No newline at end of file` + +// linePrefixes is the set of all characters a valid line in a diff +// hunk can start with. '\' can appear in diffs when no newline is +// present at the end of a file. +// See: 'http://www.gnu.org/software/diffutils/manual/diffutils.html#Incomplete-Lines' +var linePrefixes = []byte{' ', '-', '+', '\\'} + +// linePrefix returns true if 'c' is in 'linePrefixes'. +func linePrefix(c byte) bool { + for _, p := range linePrefixes { + if p == c { + return true + } + } + return false +} + +// normalizeHeader takes a header of the form: +// "@@ -linestart[,chunksize] +linestart[,chunksize] @@ section" +// and returns two strings, with the first in the form: +// "@@ -linestart,chunksize +linestart,chunksize @@". +// where linestart and chunksize are both integers. The second is the +// optional section header. chunksize may be omitted from the header +// if its value is 1. normalizeHeader returns an error if the header +// is not in the correct format. +func normalizeHeader(header string) (string, string, error) { + // Split the header into five parts: the first '@@', the two + // ranges, the last '@@', and the optional section. + pieces := strings.SplitN(header, " ", 5) + if len(pieces) < 4 { + return "", "", &ErrBadHunkHeader{header: header} + } + + if pieces[0] != "@@" { + return "", "", &ErrBadHunkHeader{header: header} + } + for i := 1; i < 3; i++ { + if !strings.ContainsRune(pieces[i], ',') { + pieces[i] = pieces[i] + ",1" + } + } + if pieces[3] != "@@" { + return "", "", &ErrBadHunkHeader{header: header} + } + + var section string + if len(pieces) == 5 { + section = pieces[4] + } + return strings.Join(pieces, " "), strings.TrimSpace(section), nil +} + +// ReadAllHunks reads all remaining hunks from r. A successful call +// returns err == nil, not err == EOF. Because ReadAllHunks is defined +// to read until EOF, it does not treat end of file as an error to be +// reported. +func (r *HunksReader) ReadAllHunks() ([]*Hunk, error) { + var hunks []*Hunk + linesRead := int32(0) + for { + hunk, err := r.ReadHunk() + if err == io.EOF { + return hunks, nil + } + if hunk != nil { + linesRead++ // account for the hunk header line + hunk.StartPosition = linesRead + hunks = append(hunks, hunk) + linesRead += int32(bytes.Count(hunk.Body, []byte{'\n'})) + } + if err != nil { + return hunks, err + } + } +} + +// A ParseError is a description of a unified diff syntax error. +type ParseError struct { + Line int // Line where the error occurred + Offset int64 // Offset where the error occurred + Err error // The actual error +} + +func (e *ParseError) Error() string { + return fmt.Sprintf("line %d, char %d: %s", e.Line, e.Offset, e.Err) +} + +// ErrNoHunkHeader indicates that a unified diff hunk header was +// expected but not found during parsing. +var ErrNoHunkHeader = errors.New("no hunk header") + +// ErrBadHunkHeader indicates that a malformed unified diff hunk +// header was encountered during parsing. +type ErrBadHunkHeader struct { + header string +} + +func (e *ErrBadHunkHeader) Error() string { + if e.header == "" { + return "bad hunk header" + } + return "bad hunk header: " + e.header +} + +// ErrBadHunkLine is when a line not beginning with ' ', '-', '+', or +// '\' is encountered while reading a hunk. In the context of reading +// a single hunk or file, it is an unexpected error. In a multi-file +// diff, however, it indicates that the current file's diff is +// complete (and remaining diff data will describe another file +// unified diff). +type ErrBadHunkLine struct { + Line []byte +} + +func (e *ErrBadHunkLine) Error() string { + m := "bad hunk line (does not start with ' ', '-', '+', or '\\')" + if len(e.Line) == 0 { + return m + } + return m + ": " + string(e.Line) +} diff --git a/vendor/github.com/sourcegraph/go-diff/diff/print.go b/vendor/github.com/sourcegraph/go-diff/diff/print.go new file mode 100644 index 000000000..d440cb9ad --- /dev/null +++ b/vendor/github.com/sourcegraph/go-diff/diff/print.go @@ -0,0 +1,140 @@ +package diff + +import ( + "bytes" + "fmt" + "io" + "time" + + "sourcegraph.com/sqs/pbtypes" +) + +// PrintMultiFileDiff prints a multi-file diff in unified diff format. +func PrintMultiFileDiff(ds []*FileDiff) ([]byte, error) { + var buf bytes.Buffer + for _, d := range ds { + diff, err := PrintFileDiff(d) + if err != nil { + return nil, err + } + if _, err := buf.Write(diff); err != nil { + return nil, err + } + } + return buf.Bytes(), nil +} + +// PrintFileDiff prints a FileDiff in unified diff format. +// +// TODO(sqs): handle escaping whitespace/etc. chars in filenames +func PrintFileDiff(d *FileDiff) ([]byte, error) { + var buf bytes.Buffer + + for _, xheader := range d.Extended { + if _, err := fmt.Fprintln(&buf, xheader); err != nil { + return nil, err + } + } + + if d.Hunks == nil { + return buf.Bytes(), nil + } + + if err := printFileHeader(&buf, "--- ", d.OrigName, timePtr(d.OrigTime)); err != nil { + return nil, err + } + if err := printFileHeader(&buf, "+++ ", d.NewName, timePtr(d.NewTime)); err != nil { + return nil, err + } + + ph, err := PrintHunks(d.Hunks) + if err != nil { + return nil, err + } + + if _, err := buf.Write(ph); err != nil { + return nil, err + } + return buf.Bytes(), nil +} + +func timePtr(ts *pbtypes.Timestamp) *time.Time { + if ts == nil { + return nil + } + t := ts.Time() + return &t +} + +func printFileHeader(w io.Writer, prefix string, filename string, timestamp *time.Time) error { + if _, err := fmt.Fprint(w, prefix, filename); err != nil { + return err + } + if timestamp != nil { + if _, err := fmt.Fprint(w, "\t", timestamp.Format(diffTimeFormatLayout)); err != nil { + return err + } + } + if _, err := fmt.Fprintln(w); err != nil { + return err + } + return nil +} + +// PrintHunks prints diff hunks in unified diff format. +func PrintHunks(hunks []*Hunk) ([]byte, error) { + var buf bytes.Buffer + for _, hunk := range hunks { + _, err := fmt.Fprintf(&buf, + "@@ -%d,%d +%d,%d @@", hunk.OrigStartLine, hunk.OrigLines, hunk.NewStartLine, hunk.NewLines, + ) + if err != nil { + return nil, err + } + if hunk.Section != "" { + _, err := fmt.Fprint(&buf, " ", hunk.Section) + if err != nil { + return nil, err + } + } + if _, err := fmt.Fprintln(&buf); err != nil { + return nil, err + } + + if hunk.OrigNoNewlineAt == 0 { + if _, err := buf.Write(hunk.Body); err != nil { + return nil, err + } + } else { + if _, err := buf.Write(hunk.Body[:hunk.OrigNoNewlineAt]); err != nil { + return nil, err + } + if err := printNoNewlineMessage(&buf); err != nil { + return nil, err + } + if _, err := buf.Write(hunk.Body[hunk.OrigNoNewlineAt:]); err != nil { + return nil, err + } + } + + if !bytes.HasSuffix(hunk.Body, []byte{'\n'}) { + if _, err := fmt.Fprintln(&buf); err != nil { + return nil, err + } + if err := printNoNewlineMessage(&buf); err != nil { + return nil, err + } + } + } + return buf.Bytes(), nil +} + +func printNoNewlineMessage(w io.Writer) error { + if _, err := w.Write([]byte(noNewlineMessage)); err != nil { + return err + } + if _, err := fmt.Fprintln(w); err != nil { + return err + } + return nil +} diff --git a/vendor/github.com/sourcegraph/go-diff/diff/reader_util.go b/vendor/github.com/sourcegraph/go-diff/diff/reader_util.go new file mode 100644 index 000000000..395fb7baf --- /dev/null +++ b/vendor/github.com/sourcegraph/go-diff/diff/reader_util.go @@ -0,0 +1,37 @@ +package diff + +import ( + "bufio" + "io" +) + +// readLine is a helper that mimics the functionality of calling bufio.Scanner.Scan() and +// bufio.Scanner.Bytes(), but without the token size limitation. It will read and return +// the next line in the Reader with the trailing newline stripped. It will return an +// io.EOF error when there is nothing left to read (at the start of the function call). It +// will return any other errors it receives from the underlying call to ReadBytes. +func readLine(r *bufio.Reader) ([]byte, error) { + line_, err := r.ReadBytes('\n') + if err == io.EOF { + if len(line_) == 0 { + return nil, io.EOF + } + + // ReadBytes returned io.EOF, because it didn't find another newline, but there is + // still the remainder of the file to return as a line. + line := line_ + return line, nil + } else if err != nil { + return nil, err + } + line := line_[0 : len(line_)-1] + return dropCR(line), nil +} + +// dropCR drops a terminal \r from the data. +func dropCR(data []byte) []byte { + if len(data) > 0 && data[len(data)-1] == '\r' { + return data[0 : len(data)-1] + } + return data +} -- cgit mrf-deployment