diff options
Diffstat (limited to 'pkg/commands/patch/patch.go')
-rw-r--r-- | pkg/commands/patch/patch.go | 151 |
1 files changed, 151 insertions, 0 deletions
diff --git a/pkg/commands/patch/patch.go b/pkg/commands/patch/patch.go new file mode 100644 index 000000000..5275fb613 --- /dev/null +++ b/pkg/commands/patch/patch.go @@ -0,0 +1,151 @@ +package patch + +import ( + "github.com/jesseduffield/lazygit/pkg/utils" + "github.com/samber/lo" +) + +type Patch struct { + // header of the patch (split on newlines) e.g. + // diff --git a/filename b/filename + // index dcd3485..1ba5540 100644 + // --- a/filename + // +++ b/filename + header []string + // hunks of the patch + hunks []*Hunk +} + +// Returns a new patch with the specified transformation applied (e.g. +// only selecting a subset of changes). +// Leaves the original patch unchanged. +func (self *Patch) Transform(opts TransformOpts) *Patch { + return transform(self, opts) +} + +// Returns the patch as a plain string +func (self *Patch) FormatPlain() string { + return formatPlain(self) +} + +// Returns a range of lines from the patch as a plain string (range is inclusive) +func (self *Patch) FormatRangePlain(startIdx int, endIdx int) string { + return formatRangePlain(self, startIdx, endIdx) +} + +// Returns the patch as a string with ANSI color codes for displaying in a view +func (self *Patch) FormatView(opts FormatViewOpts) string { + return formatView(self, opts) +} + +// Returns the lines of the patch +func (self *Patch) Lines() []*PatchLine { + lines := []*PatchLine{} + for _, line := range self.header { + lines = append(lines, &PatchLine{Content: line, Kind: PATCH_HEADER}) + } + + for _, hunk := range self.hunks { + lines = append(lines, hunk.allLines()...) + } + + return lines +} + +// Returns the patch line index of the first line in the given hunk +func (self *Patch) HunkStartIdx(hunkIndex int) int { + hunkIndex = utils.Clamp(hunkIndex, 0, len(self.hunks)-1) + + result := len(self.header) + for i := 0; i < hunkIndex; i++ { + result += self.hunks[i].lineCount() + } + return result +} + +// Returns the patch line index of the last line in the given hunk +func (self *Patch) HunkEndIdx(hunkIndex int) int { + hunkIndex = utils.Clamp(hunkIndex, 0, len(self.hunks)-1) + + return self.HunkStartIdx(hunkIndex) + self.hunks[hunkIndex].lineCount() - 1 +} + +func (self *Patch) ContainsChanges() bool { + return lo.SomeBy(self.hunks, func(hunk *Hunk) bool { + return hunk.containsChanges() + }) +} + +// Takes a line index in the patch and returns the line number in the new file. +// If the line is a header line, returns 1. +// If the line is a hunk header line, returns the first file line number in that hunk. +// If the line is out of range below, returns the last file line number in the last hunk. +func (self *Patch) LineNumberOfLine(idx int) int { + if idx < len(self.header) || len(self.hunks) == 0 { + return 1 + } + + hunkIdx := self.HunkContainingLine(idx) + // cursor out of range, return last file line number + if hunkIdx == -1 { + lastHunk := self.hunks[len(self.hunks)-1] + return lastHunk.newStart + lastHunk.newLength() - 1 + } + + hunk := self.hunks[hunkIdx] + hunkStartIdx := self.HunkStartIdx(hunkIdx) + idxInHunk := idx - hunkStartIdx + + if idxInHunk == 0 { + return hunk.oldStart + } + + lines := hunk.bodyLines[:idxInHunk-1] + offset := nLinesWithKind(lines, []PatchLineKind{ADDITION, CONTEXT}) + return hunk.oldStart + offset +} + +// Returns hunk index containing the line at the given patch line index +func (self *Patch) HunkContainingLine(idx int) int { + for hunkIdx, hunk := range self.hunks { + hunkStartIdx := self.HunkStartIdx(hunkIdx) + if idx >= hunkStartIdx && idx < hunkStartIdx+hunk.lineCount() { + return hunkIdx + } + } + return -1 +} + +// Returns the patch line index of the next change (i.e. addition or deletion). +func (self *Patch) GetNextChangeIdx(idx int) int { + idx = utils.Clamp(idx, 0, self.LineCount()-1) + + lines := self.Lines() + + for i, line := range lines[idx:] { + if line.isChange() { + return i + idx + } + } + + // there are no changes from the cursor onwards so we'll instead + // return the index of the last change + for i := len(lines) - 1; i >= 0; i-- { + line := lines[i] + if line.isChange() { + return i + } + } + + // should not be possible + return 0 +} + +// Returns the length of the patch in lines +func (self *Patch) LineCount() int { + count := len(self.header) + for _, hunk := range self.hunks { + count += hunk.lineCount() + } + return count +} |