@@ -79,14 +79,8 @@ func (p *parser) ParseTextChunk(frag *TextFragment) error {
7979 return p .Errorf (0 , "no content following fragment header" )
8080 }
8181
82- isNoNewlineLine := func (s string ) bool {
83- // test for "\ No newline at end of file" by prefix because the text
84- // changes by locale (git claims all versions are at least 12 chars)
85- return len (s ) >= 12 && s [:2 ] == "\\ "
86- }
87-
8882 oldLines , newLines := frag .OldLines , frag .NewLines
89- for {
83+ for oldLines > 0 || newLines > 0 {
9084 line := p .Line (0 )
9185 op , data := line [0 ], line [1 :]
9286
@@ -113,13 +107,14 @@ func (p *parser) ParseTextChunk(frag *TextFragment) error {
113107 frag .LinesAdded ++
114108 frag .TrailingContext = 0
115109 frag .Lines = append (frag .Lines , Line {OpAdd , data })
116- default :
110+ case '\\' :
117111 // this may appear in middle of fragment if it's for a deleted line
118- if isNoNewlineLine (line ) {
119- last := & frag .Lines [len (frag .Lines )- 1 ]
120- last .Line = strings .TrimSuffix (last .Line , "\n " )
112+ if isNoNewlineMarker (line ) {
113+ removeLastNewline (frag )
121114 break
122115 }
116+ fallthrough
117+ default :
123118 // TODO(bkeyes): if this is because we hit the next header, it
124119 // would be helpful to return the miscounts line error. We could
125120 // either test for the common headers ("@@ -", "diff --git") or
@@ -128,11 +123,6 @@ func (p *parser) ParseTextChunk(frag *TextFragment) error {
128123 return p .Errorf (0 , "invalid line operation: %q" , op )
129124 }
130125
131- next := p .Line (1 )
132- if oldLines <= 0 && newLines <= 0 && ! isNoNewlineLine (next ) {
133- break
134- }
135-
136126 if err := p .Next (); err != nil {
137127 if err == io .EOF {
138128 break
@@ -145,13 +135,35 @@ func (p *parser) ParseTextChunk(frag *TextFragment) error {
145135 hdr := max (frag .OldLines - oldLines , frag .NewLines - newLines ) + 1
146136 return p .Errorf (- hdr , "fragment header miscounts lines: %+d old, %+d new" , - oldLines , - newLines )
147137 }
138+ if frag .LinesAdded == 0 && frag .LinesDeleted == 0 {
139+ return p .Errorf (0 , "fragment contains no changes" )
140+ }
148141
149- if err := p .Next (); err != nil && err != io .EOF {
150- return err
142+ // check for a final "no newline" marker since it is not included in the
143+ // counters used to stop the loop above
144+ if isNoNewlineMarker (p .Line (0 )) {
145+ removeLastNewline (frag )
146+ if err := p .Next (); err != nil && err != io .EOF {
147+ return err
148+ }
151149 }
150+
152151 return nil
153152}
154153
154+ func isNoNewlineMarker (s string ) bool {
155+ // test for "\ No newline at end of file" by prefix because the text
156+ // changes by locale (git claims all versions are at least 12 chars)
157+ return len (s ) >= 12 && s [:2 ] == "\\ "
158+ }
159+
160+ func removeLastNewline (frag * TextFragment ) {
161+ if len (frag .Lines ) > 0 {
162+ last := & frag .Lines [len (frag .Lines )- 1 ]
163+ last .Line = strings .TrimSuffix (last .Line , "\n " )
164+ }
165+ }
166+
155167func parseRange (s string ) (start int64 , end int64 , err error ) {
156168 parts := strings .SplitN (s , "," , 2 )
157169
0 commit comments