1
0
mirror of https://github.com/jesseduffield/lazygit.git synced 2024-12-14 11:23:09 +02:00
lazygit/pkg/commands/patch/patch_modifier.go

166 lines
4.4 KiB
Go
Raw Normal View History

2020-08-15 03:18:40 +02:00
package patch
2018-12-02 08:29:17 +02:00
import (
"fmt"
2018-12-02 08:29:17 +02:00
"regexp"
"strings"
"github.com/sirupsen/logrus"
)
var hunkHeaderRegexp = regexp.MustCompile(`(?m)^@@ -(\d+)[^\+]+\+(\d+)[^@]+@@(.*)$`)
var patchHeaderRegexp = regexp.MustCompile(`(?ms)(^diff.*?)^@@`)
func GetHeaderFromDiff(diff string) string {
match := patchHeaderRegexp.FindStringSubmatch(diff)
if len(match) <= 1 {
return ""
}
return match[1]
}
func GetHunksFromDiff(diff string) []*PatchHunk {
hunks := []*PatchHunk{}
firstLineIdx := -1
var hunkLines []string
pastDiffHeader := false
for lineIdx, line := range strings.SplitAfter(diff, "\n") {
isHunkHeader := strings.HasPrefix(line, "@@ -")
if isHunkHeader {
if pastDiffHeader { // we need to persist the current hunk
hunks = append(hunks, newHunk(hunkLines, firstLineIdx))
}
pastDiffHeader = true
firstLineIdx = lineIdx
hunkLines = []string{line}
continue
}
if !pastDiffHeader { // skip through the stuff that precedes the first hunk
continue
2018-12-02 08:29:17 +02:00
}
hunkLines = append(hunkLines, line)
2018-12-02 08:29:17 +02:00
}
2018-12-05 10:33:46 +02:00
if pastDiffHeader {
hunks = append(hunks, newHunk(hunkLines, firstLineIdx))
}
return hunks
2018-12-02 08:29:17 +02:00
}
type PatchModifier struct {
Log *logrus.Entry
filename string
hunks []*PatchHunk
header string
}
func NewPatchModifier(log *logrus.Entry, filename string, diffText string) *PatchModifier {
return &PatchModifier{
Log: log,
filename: filename,
hunks: GetHunksFromDiff(diffText),
header: GetHeaderFromDiff(diffText),
}
}
func (d *PatchModifier) ModifiedPatchForLines(lineIndices []int, reverse bool, keepOriginalHeader bool) string {
// step one is getting only those hunks which we care about
hunksInRange := []*PatchHunk{}
2019-11-04 02:46:28 +02:00
outer:
for _, hunk := range d.hunks {
2019-11-04 02:46:28 +02:00
// if there is any line in our lineIndices array that the hunk contains, we append it
for _, lineIdx := range lineIndices {
if lineIdx >= hunk.FirstLineIdx && lineIdx <= hunk.LastLineIdx() {
2019-11-04 02:46:28 +02:00
hunksInRange = append(hunksInRange, hunk)
continue outer
}
2018-12-02 08:29:17 +02:00
}
}
// step 2 is collecting all the hunks with new headers
startOffset := 0
formattedHunks := ""
var formattedHunk string
for _, hunk := range hunksInRange {
2019-11-04 02:46:28 +02:00
startOffset, formattedHunk = hunk.formatWithChanges(lineIndices, reverse, startOffset)
formattedHunks += formattedHunk
}
if formattedHunks == "" {
return ""
2018-12-02 08:29:17 +02:00
}
var fileHeader string
// for staging/unstaging lines we don't want the original header because
// it makes git confused e.g. when dealing with deleted/added files
// but with building and applying patches the original header gives git
// information it needs to cleanly apply patches
if keepOriginalHeader {
fileHeader = d.header
} else {
fileHeader = fmt.Sprintf("--- a/%s\n+++ b/%s\n", d.filename, d.filename)
}
return fileHeader + formattedHunks
2018-12-02 08:29:17 +02:00
}
func (d *PatchModifier) ModifiedPatchForRange(firstLineIdx int, lastLineIdx int, reverse bool, keepOriginalHeader bool) string {
2019-11-04 02:46:28 +02:00
// generate array of consecutive line indices from our range
selectedLines := []int{}
for i := firstLineIdx; i <= lastLineIdx; i++ {
selectedLines = append(selectedLines, i)
}
return d.ModifiedPatchForLines(selectedLines, reverse, keepOriginalHeader)
}
func (d *PatchModifier) OriginalPatchLength() int {
if len(d.hunks) == 0 {
return 0
}
return d.hunks[len(d.hunks)-1].LastLineIdx()
2019-11-04 02:46:28 +02:00
}
func ModifiedPatchForRange(log *logrus.Entry, filename string, diffText string, firstLineIdx int, lastLineIdx int, reverse bool, keepOriginalHeader bool) string {
p := NewPatchModifier(log, filename, diffText)
return p.ModifiedPatchForRange(firstLineIdx, lastLineIdx, reverse, keepOriginalHeader)
2018-12-02 08:29:17 +02:00
}
2020-08-09 07:42:20 +02:00
func ModifiedPatchForLines(log *logrus.Entry, filename string, diffText string, includedLineIndices []int, reverse bool, keepOriginalHeader bool) string {
p := NewPatchModifier(log, filename, diffText)
return p.ModifiedPatchForLines(includedLineIndices, reverse, keepOriginalHeader)
}
// I want to know, given a hunk, what line a given index is on
2021-08-04 11:43:34 +02:00
func (hunk *PatchHunk) LineNumberOfLine(idx int) int {
2021-08-03 15:00:28 +02:00
n := idx - hunk.FirstLineIdx - 1
2021-08-04 11:43:34 +02:00
if n < 0 {
n = 0
} else if n >= len(hunk.bodyLines) {
n = len(hunk.bodyLines) - 1
2021-08-03 15:00:28 +02:00
}
lines := hunk.bodyLines[0:n]
offset := nLinesWithPrefix(lines, []string{"+", " "})
2021-08-04 11:43:34 +02:00
return hunk.newStart + offset
}
func nLinesWithPrefix(lines []string, chars []string) int {
result := 0
for _, line := range lines {
for _, char := range chars {
if line[:1] == char {
result++
}
}
}
return result
}