mirror of
https://github.com/jesseduffield/lazygit.git
synced 2024-12-14 11:23:09 +02:00
152 lines
4.0 KiB
Go
152 lines
4.0 KiB
Go
|
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
|
||
|
}
|