mirror of
https://github.com/jesseduffield/lazygit.git
synced 2025-07-05 00:59:19 +02:00
It is confusing to get header lines, hunk headers, or context lines rendered as being included in a custom patch, when including these makes no difference to the patch. This is only a visual change; internally, we still record these non-patch lines as being included in the patch. It doesn't matter though; you can press space on a header line and nothing happens. It would probably be cleaner to only record + and - lines in the includedLines array, but that would be a bit more work, and doesn't seem worth it.
147 lines
3.7 KiB
Go
147 lines
3.7 KiB
Go
package patch
|
|
|
|
import (
|
|
"strings"
|
|
|
|
"github.com/jesseduffield/generics/set"
|
|
"github.com/jesseduffield/lazygit/pkg/gui/style"
|
|
"github.com/jesseduffield/lazygit/pkg/theme"
|
|
"github.com/samber/lo"
|
|
)
|
|
|
|
type patchPresenter struct {
|
|
patch *Patch
|
|
// if true, all following fields are ignored
|
|
plain bool
|
|
|
|
// line indices for tagged lines (e.g. lines added to a custom patch)
|
|
incLineIndices *set.Set[int]
|
|
}
|
|
|
|
// formats the patch as a plain string
|
|
func formatPlain(patch *Patch) string {
|
|
presenter := &patchPresenter{
|
|
patch: patch,
|
|
plain: true,
|
|
incLineIndices: set.New[int](),
|
|
}
|
|
return presenter.format()
|
|
}
|
|
|
|
func formatRangePlain(patch *Patch, startIdx int, endIdx int) string {
|
|
lines := patch.Lines()[startIdx : endIdx+1]
|
|
return strings.Join(
|
|
lo.Map(lines, func(line *PatchLine, _ int) string {
|
|
return line.Content + "\n"
|
|
}),
|
|
"",
|
|
)
|
|
}
|
|
|
|
type FormatViewOpts struct {
|
|
// line indices for tagged lines (e.g. lines added to a custom patch)
|
|
IncLineIndices *set.Set[int]
|
|
}
|
|
|
|
// formats the patch for rendering within a view, meaning it's coloured and
|
|
// highlights selected items
|
|
func formatView(patch *Patch, opts FormatViewOpts) string {
|
|
includedLineIndices := opts.IncLineIndices
|
|
if includedLineIndices == nil {
|
|
includedLineIndices = set.New[int]()
|
|
}
|
|
presenter := &patchPresenter{
|
|
patch: patch,
|
|
plain: false,
|
|
incLineIndices: includedLineIndices,
|
|
}
|
|
return presenter.format()
|
|
}
|
|
|
|
func (self *patchPresenter) format() string {
|
|
// if we have no changes in our patch (i.e. no additions or deletions) then
|
|
// the patch is effectively empty and we can return an empty string
|
|
if !self.patch.ContainsChanges() {
|
|
return ""
|
|
}
|
|
|
|
stringBuilder := &strings.Builder{}
|
|
lineIdx := 0
|
|
appendLine := func(line string) {
|
|
_, _ = stringBuilder.WriteString(line + "\n")
|
|
|
|
lineIdx++
|
|
}
|
|
|
|
for _, line := range self.patch.header {
|
|
// always passing false for 'included' here because header lines are not part of the patch
|
|
appendLine(self.formatLineAux(line, theme.DefaultTextColor.SetBold(), false))
|
|
}
|
|
|
|
for _, hunk := range self.patch.hunks {
|
|
appendLine(
|
|
self.formatLineAux(
|
|
hunk.formatHeaderStart(),
|
|
style.FgCyan,
|
|
false,
|
|
) +
|
|
// we're splitting the line into two parts: the diff header and the context
|
|
// We explicitly pass 'included' as false for both because these are not part
|
|
// of the actual patch
|
|
self.formatLineAux(
|
|
hunk.headerContext,
|
|
theme.DefaultTextColor,
|
|
false,
|
|
),
|
|
)
|
|
|
|
for _, line := range hunk.bodyLines {
|
|
style := self.patchLineStyle(line)
|
|
if line.isChange() {
|
|
appendLine(self.formatLine(line.Content, style, lineIdx))
|
|
} else {
|
|
appendLine(self.formatLineAux(line.Content, style, false))
|
|
}
|
|
}
|
|
}
|
|
|
|
return stringBuilder.String()
|
|
}
|
|
|
|
func (self *patchPresenter) patchLineStyle(patchLine *PatchLine) style.TextStyle {
|
|
switch patchLine.Kind {
|
|
case ADDITION:
|
|
return style.FgGreen
|
|
case DELETION:
|
|
return style.FgRed
|
|
default:
|
|
return theme.DefaultTextColor
|
|
}
|
|
}
|
|
|
|
func (self *patchPresenter) formatLine(str string, textStyle style.TextStyle, index int) string {
|
|
included := self.incLineIndices.Includes(index)
|
|
|
|
return self.formatLineAux(str, textStyle, included)
|
|
}
|
|
|
|
// 'selected' means you've got it highlighted with your cursor
|
|
// 'included' means the line has been included in the patch (only applicable when
|
|
// building a patch)
|
|
func (self *patchPresenter) formatLineAux(str string, textStyle style.TextStyle, included bool) string {
|
|
if self.plain {
|
|
return str
|
|
}
|
|
|
|
firstCharStyle := textStyle
|
|
if included {
|
|
firstCharStyle = firstCharStyle.MergeStyle(style.BgGreen)
|
|
}
|
|
|
|
if len(str) < 2 {
|
|
return firstCharStyle.Sprint(str)
|
|
}
|
|
|
|
return firstCharStyle.Sprint(str[:1]) + textStyle.Sprint(str[1:])
|
|
}
|