this repo has no description
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Accept comments when parsing fragment headers

Also parse the header directly instead of using a regexp. This allows
finer-grained errors.

+40 -29
+2
gitdiff/gitdiff.go
··· 27 27 28 28 // Fragment describes changed lines starting at a specific line in a text file. 29 29 type Fragment struct { 30 + Comment string 31 + 30 32 OldPosition int64 31 33 OldLines int64 32 34
+35 -27
gitdiff/parser.go
··· 4 4 "bufio" 5 5 "fmt" 6 6 "io" 7 - "regexp" 8 7 "strconv" 9 8 "strings" 10 9 ) ··· 54 53 newFilePrefix = "+++ " 55 54 56 55 devNull = "/dev/null" 57 - ) 58 - 59 - var ( 60 - // TODO(bkeyes): are the boundary conditions necessary? 61 - fragmentHeaderRegexp = regexp.MustCompile(`^@@ -(\d+),(\d+) \+(\d+)(?:,(\d+))? @@.*\n`) 62 56 ) 63 57 64 58 // ParseNextFileHeader finds and parses the next file header in the stream. It ··· 166 160 return len(line) >= len(shortestValidHeader) && strings.HasPrefix(line, fragmentHeaderPrefix) 167 161 } 168 162 169 - func parseFragmentHeader(f *Fragment, header string) error { 170 - // TODO(bkeyes): use strings.FieldsFunc instead of regexp 171 - match := fragmentHeaderRegexp.FindStringSubmatch(header) 172 - if len(match) < 5 { 163 + // TODO(bkeyes): fix duplication with isMaybeFragmentHeader 164 + func parseFragmentHeader(f *Fragment, header string) (err error) { 165 + const startMark = "@@ " 166 + const endMark = " @@" 167 + 168 + parts := strings.SplitAfterN(header, endMark, 2) 169 + if len(parts) < 2 || !strings.HasPrefix(parts[0], startMark) || !strings.HasSuffix(parts[0], endMark) { 173 170 return fmt.Errorf("invalid fragment header") 174 171 } 175 172 176 - parseInt := func(s string, v *int64) (err error) { 177 - if *v, err = strconv.ParseInt(s, 10, 64); err != nil { 178 - nerr := err.(*strconv.NumError) 179 - return fmt.Errorf("invalid fragment header value: %s: %v", s, nerr.Err) 180 - } 181 - return 173 + header = parts[0][len(startMark) : len(parts[0])-len(endMark)] 174 + f.Comment = strings.TrimSpace(parts[1]) 175 + 176 + ranges := strings.Split(header, " ") 177 + if len(ranges) != 2 { 178 + return fmt.Errorf("invalid fragment header") 182 179 } 183 180 184 - if err := parseInt(match[1], &f.OldPosition); err != nil { 185 - return err 181 + if !strings.HasPrefix(ranges[0], "-") || !strings.HasPrefix(ranges[1], "+") { 182 + return fmt.Errorf("invalid fragment header: bad range marker") 186 183 } 187 - if err := parseInt(match[2], &f.OldLines); err != nil { 188 - return err 184 + if f.OldPosition, f.OldLines, err = parseRange(ranges[0][1:]); err != nil { 185 + return fmt.Errorf("invalid fragment header: %v", err) 186 + } 187 + if f.NewPosition, f.NewLines, err = parseRange(ranges[1][1:]); err != nil { 188 + return fmt.Errorf("invalid fragment header: %v", err) 189 189 } 190 + return nil 191 + } 192 + 193 + func parseRange(s string) (start int64, end int64, err error) { 194 + parts := strings.SplitN(s, ",", 2) 190 195 191 - if err := parseInt(match[3], &f.NewPosition); err != nil { 192 - return err 196 + if start, err = strconv.ParseInt(parts[0], 10, 64); err != nil { 197 + nerr := err.(*strconv.NumError) 198 + return 0, 0, fmt.Errorf("bad start of range: %s: %v", parts[0], nerr.Err) 193 199 } 194 200 195 - f.NewLines = 1 196 - if match[4] != "" { 197 - if err := parseInt(match[4], &f.NewLines); err != nil { 198 - return err 201 + if len(parts) > 1 { 202 + if end, err = strconv.ParseInt(parts[1], 10, 64); err != nil { 203 + nerr := err.(*strconv.NumError) 204 + return 0, 0, fmt.Errorf("bad end of range: %s: %v", parts[1], nerr.Err) 199 205 } 206 + } else { 207 + end = 1 200 208 } 201 209 202 - return nil 210 + return 203 211 }
+3 -2
gitdiff/parser_test.go
··· 89 89 NewLines: 9, 90 90 }, 91 91 }, 92 - "trailingWhitespace": { 93 - Input: "@@ -21,5 +28,9 @@ \r\n", 92 + "trailingComment": { 93 + Input: "@@ -21,5 +28,9 @@ func test(n int) {\n", 94 94 Output: &Fragment{ 95 + Comment: "func test(n int) {", 95 96 OldPosition: 21, 96 97 OldLines: 5, 97 98 NewPosition: 28,