···35353636 NewPosition int64
3737 NewLines int64
3838+3939+ LinesAdded int64
4040+ LinesDeleted int64
4141+4242+ Lines []FragmentLine
3843}
4444+4545+// FragmentLine is a line in a fragment.
4646+type FragmentLine struct {
4747+ Op LineOp
4848+ Line string
4949+}
5050+5151+// LineOp describes the type of a fragment line: context, added, or removed.
5252+type LineOp int
5353+5454+const (
5555+ // OpContext indicates a context line
5656+ OpContext LineOp = iota
5757+ // OpDelete indicates a deleted line
5858+ OpDelete
5959+ // OpAdd indicates an added line
6060+ OpAdd
6161+)
39624063// Header returns the cannonical header of this fragment.
4164func (f *Fragment) Header() string {
+62-7
gitdiff/parser.go
···4545// by allowing users to set or override defaults
46464747// parser invariants:
4848-// - methods that parse objects start on the first line of the first object
4949-// - methods that parse objects return on the first line after the last object
5050-// - if a parse method returns a nil object, the parser was not advanced
4848+// - methods that parse objects:
4949+// - start with the parser on the first line of the first object
5050+// - if returning nil, do not advance
5151+// - if returning an error, do not advance past the object
5252+// - if returning an object, advance to the first line after the object
5153// - any exported parsing methods must initialize the parser by calling Next()
52545355type parser struct {
···170172171173 parts := strings.SplitAfterN(p.Line(0), endMark, 2)
172174 if len(parts) < 2 {
173173- return nil, fmt.Errorf("invalid fragment header")
175175+ return nil, p.Errorf(0, "invalid fragment header")
174176 }
175177176178 f := &Fragment{}
···179181 header := parts[0][len(startMark) : len(parts[0])-len(endMark)]
180182 ranges := strings.Split(header, " +")
181183 if len(ranges) != 2 {
182182- return nil, fmt.Errorf("invalid fragment header")
184184+ return nil, p.Errorf(0, "invalid fragment header")
183185 }
184186185187 var err error
186188 if f.OldPosition, f.OldLines, err = parseRange(ranges[0]); err != nil {
187187- return nil, fmt.Errorf("invalid fragment header: %v", err)
189189+ return nil, p.Errorf(0, "invalid fragment header: %v", err)
188190 }
189191 if f.NewPosition, f.NewLines, err = parseRange(ranges[1]); err != nil {
190190- return nil, fmt.Errorf("invalid fragment header: %v", err)
192192+ return nil, p.Errorf(0, "invalid fragment header: %v", err)
191193 }
192194193195 if err := p.Next(); err != nil && err != io.EOF {
194196 return nil, err
195197 }
196198 return f, nil
199199+}
200200+201201+func (p *parser) ParseFragment() (*Fragment, error) {
202202+ frag, err := p.ParseFragmentHeader()
203203+ if err != nil {
204204+ return nil, err
205205+ }
206206+ if frag == nil {
207207+ return nil, nil
208208+ }
209209+ if p.Line(0) == "" {
210210+ return nil, p.Errorf(0, "no content following fragment header")
211211+ }
212212+213213+ oldLines, newLines := frag.OldLines, frag.NewLines
214214+ for oldLines > 0 && newLines > 0 {
215215+ line := p.Line(0)
216216+ switch line[0] {
217217+ case '\n':
218218+ fallthrough // newer GNU diff versions create empty context lines
219219+ case ' ':
220220+ oldLines--
221221+ newLines--
222222+ frag.Lines = append(frag.Lines, FragmentLine{OpContext, line[1:]})
223223+ case '-':
224224+ frag.LinesDeleted++
225225+ oldLines--
226226+ frag.Lines = append(frag.Lines, FragmentLine{OpDelete, line[1:]})
227227+ case '+':
228228+ frag.LinesAdded++
229229+ newLines--
230230+ frag.Lines = append(frag.Lines, FragmentLine{OpAdd, line[1:]})
231231+ default:
232232+ // this could be "\ No newline at end of file", which we allow
233233+ // only check the prefix because the text changes by locale
234234+ // git also asserts that any translation is at least 12 characters
235235+ if len(line) >= 12 && strings.HasPrefix("\\ ", line) {
236236+ break
237237+ }
238238+ return nil, p.Errorf(0, "invalid fragment line")
239239+ }
240240+241241+ if err := p.Next(); err != nil {
242242+ if err == io.EOF {
243243+ break
244244+ }
245245+ return nil, err
246246+ }
247247+ }
248248+249249+ // TODO(bkeyes): verify stuff about the fragment
250250+251251+ return frag, nil
197252}
198253199254func parseRange(s string) (start int64, end int64, err error) {