package gitea import ( "strconv" "strings" ) // DiffLineRanges maps filenames to the set of new-file line numbers present in the diff. type DiffLineRanges struct { files map[string]map[int]bool } // Contains reports whether the given file+line is within the diff hunks. func (d *DiffLineRanges) Contains(file string, line int) bool { if d == nil || d.files == nil { return false } lines, ok := d.files[file] if !ok { return false } return lines[line] } // ParseDiffNewLines parses a unified diff and extracts the new-file line numbers // that appear in each hunk (both added and context lines). func ParseDiffNewLines(diff string) *DiffLineRanges { result := &DiffLineRanges{files: make(map[string]map[int]bool)} var currentFile string var newLine int for _, line := range strings.Split(diff, "\n") { // Track current file from +++ header if strings.HasPrefix(line, "+++ b/") { currentFile = strings.TrimPrefix(line, "+++ b/") if result.files[currentFile] == nil { result.files[currentFile] = make(map[int]bool) } continue } if strings.HasPrefix(line, "+++ /dev/null") { currentFile = "" continue } // Parse hunk header: @@ -old,count +new,count @@ or @@ -old +new @@ if strings.HasPrefix(line, "@@") && currentFile != "" { // Extract the +N part — handle both "+10,8" and "+1" forms parts := strings.Split(line, "+") if len(parts) >= 2 { // Take everything before comma or space numStr := parts[1] if idx := strings.IndexAny(numStr, ", "); idx != -1 { numStr = numStr[:idx] } n, err := strconv.Atoi(numStr) if err == nil { newLine = n } } continue } if currentFile == "" { continue } // Skip diff metadata lines if strings.HasPrefix(line, "\\") { continue } // Count lines in hunk if strings.HasPrefix(line, "+") || strings.HasPrefix(line, " ") { result.files[currentFile][newLine] = true newLine++ } else if strings.HasPrefix(line, "-") { // Removed lines don't advance new line counter continue } } return result }