mirror of
https://github.com/jesseduffield/lazygit.git
synced 2026-09-10 07:36:27 -04:00
Extend the fill background to wrapped tail segments
Tools like delta paint each diff line's background with '\x1b[K' so the color reaches the right edge. Up to now the '\x1b[K' handler appended (InnerWidth - cx) explicit padding cells with the fill bg so rendering picked up the color. That worked for short lines but silently degraded once content exceeded InnerWidth: the repeat count went non-positive, no cells were added, and after wrapping the partial tail segment was left without any cells carrying the fill color, so draw() fell back to the view's default bg. Record the fill colors on the source line as optional trailingFillAttributes. In the '\x1b[K' handler set them (and drop the padding-cell loop — the metadata covers both the wrap and the non-wrap cases). In draw(), once per source line, pick the trailing cell's fg/bg from the metadata if present and otherwise from the view defaults; then the inner-loop fills past-content cells with that. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
9c8a02f901
commit
51b409383c
|
|
@ -444,12 +444,28 @@ type SearchPosition struct {
|
|||
type viewLine struct {
|
||||
linesX, linesY int // coordinates relative to v.lines
|
||||
line []cell
|
||||
|
||||
// Colors used to extend the bg past this wrapped segment's content.
|
||||
// Derived at wrap time from the source line — see refreshViewLinesIfNeeded
|
||||
// for the per-segment rule.
|
||||
trailingFillAttributes *trailingFillAttributes
|
||||
}
|
||||
|
||||
// lineType is one of v.lines: the cells of a source lineType, plus any per-lineType
|
||||
// metadata about how it was terminated (added in later commits).
|
||||
// lineType is one of v.lines: the cells of a source line, plus optional
|
||||
// trailingFillAttributes recording the colors used to extend the bg
|
||||
// past the line's content when the writer emitted '\x1b[K'.
|
||||
type lineType struct {
|
||||
cells cells
|
||||
cells cells
|
||||
trailingFillAttributes *trailingFillAttributes
|
||||
}
|
||||
|
||||
// trailingFillAttributes describes the fg/bg colors that draw() should
|
||||
// use for cells past the end of a wrapped segment's content. On a source
|
||||
// line this records what the writer asked for via '\x1b[K' (and so opts
|
||||
// the line in to trailing fill at all); the per-segment values on each
|
||||
// viewLine are derived from it at wrap time.
|
||||
type trailingFillAttributes struct {
|
||||
fg, bg Attribute
|
||||
}
|
||||
|
||||
type cell struct {
|
||||
|
|
@ -953,16 +969,19 @@ func (v *View) parseInput(ch []byte, width int, x int, _ int) (bool, []cell) {
|
|||
} else {
|
||||
repeatCount := 1
|
||||
if _, ok := v.ei.instruction.(eraseInLineFromCursor); ok {
|
||||
// fill rest of line
|
||||
// Discard any old content past the cursor and record the
|
||||
// fill colors so draw() paints the trailing area with them.
|
||||
// This extends the bg to the right edge in both the
|
||||
// content-fits and content-wraps cases — for the latter,
|
||||
// the metadata is what reaches every wrapped segment past
|
||||
// the last word.
|
||||
v.ei.instructionRead()
|
||||
cx := 0
|
||||
for _, cell := range v.lines[v.wy].cells[0:v.wx] {
|
||||
cx += cell.width
|
||||
}
|
||||
repeatCount = v.InnerWidth() - cx
|
||||
ch = []byte{' '}
|
||||
width = 1
|
||||
truncateLine = true
|
||||
v.lines[v.wy].trailingFillAttributes = &trailingFillAttributes{
|
||||
fg: v.ei.curFgColor,
|
||||
bg: v.ei.curBgColor,
|
||||
}
|
||||
return truncateLine, []cell{}
|
||||
} else if isEscape {
|
||||
// do not output anything
|
||||
return truncateLine, nil
|
||||
|
|
@ -1252,6 +1271,15 @@ func (v *View) draw() {
|
|||
break
|
||||
}
|
||||
|
||||
// Decide the colors used for cells past the end of vline.line:
|
||||
// the source line's trailingFillAttributes (set by '\x1b[K') if
|
||||
// any, otherwise plain defaults.
|
||||
trailingCell := emptyCell
|
||||
if attrs := vline.trailingFillAttributes; attrs != nil {
|
||||
trailingCell.fgColor = attrs.fg
|
||||
trailingCell.bgColor = attrs.bg
|
||||
}
|
||||
|
||||
// x tracks the current x position in the view, and cellIdx tracks the
|
||||
// index of the cell. If we print a double-sized rune, we increment cellIdx
|
||||
// by one but x by two.
|
||||
|
|
@ -1274,7 +1302,7 @@ func (v *View) draw() {
|
|||
|
||||
// if we're out of cells to write, we'll just print empty cells.
|
||||
if cellIdx > len(vline.line)-1 {
|
||||
c = emptyCell
|
||||
c = trailingCell
|
||||
} else {
|
||||
c = vline.line[cellIdx]
|
||||
}
|
||||
|
|
@ -1312,7 +1340,25 @@ func (v *View) refreshViewLinesIfNeeded() {
|
|||
|
||||
ls := lineWrap(line.cells, wrap)
|
||||
for j := range ls {
|
||||
vline := viewLine{linesX: j, linesY: i, line: ls[j]}
|
||||
// Per-segment trailing fill. When the source line opted in
|
||||
// via '\x1b[K', the LAST wrapped segment uses those colors
|
||||
// directly; earlier segments use the colors of their own
|
||||
// last cell, so the trailing area matches the bg active
|
||||
// where that segment ended rather than bleeding the
|
||||
// '\x1b[K' bg back across color changes in the line.
|
||||
var attrs *trailingFillAttributes
|
||||
if line.trailingFillAttributes != nil {
|
||||
if j == len(ls)-1 {
|
||||
attrs = line.trailingFillAttributes
|
||||
} else if len(ls[j]) > 0 {
|
||||
last := ls[j][len(ls[j])-1]
|
||||
attrs = &trailingFillAttributes{fg: last.fgColor, bg: last.bgColor}
|
||||
}
|
||||
}
|
||||
vline := viewLine{
|
||||
linesX: j, linesY: i, line: ls[j],
|
||||
trailingFillAttributes: attrs,
|
||||
}
|
||||
|
||||
if lineIdx > len(v.viewLines)-1 {
|
||||
v.viewLines = append(v.viewLines, vline)
|
||||
|
|
|
|||
|
|
@ -508,10 +508,10 @@ func TestShortFilledLineExtendsBgWithoutWrap(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
// TestWrappedFilledLineExtendsBgToEdge demonstrates that when a line is
|
||||
// TestWrappedFilledLineExtendsBgToEdge verifies that when a line is
|
||||
// filled to the edge with \x1b[K (the pattern used by `delta` for diff
|
||||
// lines) but exceeds the view's inner width, every wrapped segment loses
|
||||
// the fill background past its content.
|
||||
// lines) but exceeds the view's inner width, every wrapped segment
|
||||
// extends the fill background past its content to the right edge.
|
||||
func TestWrappedFilledLineExtendsBgToEdge(t *testing.T) {
|
||||
WithSimulationScreen(t, 14, 6)
|
||||
|
||||
|
|
@ -524,24 +524,18 @@ func TestWrappedFilledLineExtendsBgToEdge(t *testing.T) {
|
|||
// Content with spaces so word wrap ends each segment before the
|
||||
// right edge: "aaa bbb ccc ddd eee" wraps at InnerWidth=10 to three
|
||||
// segments — "aaa bbb" / "ccc ddd" / "eee". Each row's trailing area
|
||||
// should pick up the red fill from \x1b[K but currently falls back to
|
||||
// the view default bg.
|
||||
// must pick up the red fill from \x1b[K.
|
||||
v.writeString("\x1b[41m" + "aaa bbb ccc ddd eee" + "\x1b[0m\x1b[41m\x1b[K\x1b[0m\n")
|
||||
v.draw()
|
||||
|
||||
// trailingFrom is 1-indexed: each row's content ends at column
|
||||
// trailingFrom[y]-1, so columns trailingFrom[y]..10 are the trailing
|
||||
// fill area where the bug shows.
|
||||
trailingFrom := []int{8, 8, 4}
|
||||
// All three wrapped rows should have the red fill background across
|
||||
// the full InnerWidth, including the trailing cells past each row's
|
||||
// last word.
|
||||
for y := 1; y <= 3; y++ {
|
||||
for x := trailingFrom[y-1]; x <= 10; x++ {
|
||||
for x := 1; x <= 10; x++ {
|
||||
_, style, _ := Screen.Get(x, y)
|
||||
/* EXPECTED:
|
||||
assert.Equal(t, color.Maroon, style.GetBackground(),
|
||||
"trailing cell at (%d, %d) should have red bg", x, y)
|
||||
ACTUAL: */
|
||||
assert.Equal(t, tcell.ColorDefault, style.GetBackground(),
|
||||
"trailing cell at (%d, %d) falls back to default bg", x, y)
|
||||
"cell at (%d, %d) should have red bg", x, y)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -572,12 +566,8 @@ func TestMulticolorWrappedFillUsesLastCellOfEachSegment(t *testing.T) {
|
|||
// 8..10 should pick up red rather than the \x1b[K's green.
|
||||
for x := 8; x <= 10; x++ {
|
||||
_, style, _ := Screen.Get(x, 1)
|
||||
/* EXPECTED:
|
||||
assert.Equal(t, color.Maroon, style.GetBackground(),
|
||||
"trailing cell at (%d, 1) should have red bg (matching segment's last cell)", x)
|
||||
ACTUAL: */
|
||||
assert.Equal(t, tcell.ColorDefault, style.GetBackground(),
|
||||
"trailing cell at (%d, 1) falls back to default bg", x)
|
||||
}
|
||||
|
||||
// Row 2's content ends with a green cell at x=3, so trailing
|
||||
|
|
@ -585,11 +575,7 @@ func TestMulticolorWrappedFillUsesLastCellOfEachSegment(t *testing.T) {
|
|||
// last cell and the \x1b[K bg — these happen to agree here).
|
||||
for x := 4; x <= 10; x++ {
|
||||
_, style, _ := Screen.Get(x, 2)
|
||||
/* EXPECTED:
|
||||
assert.Equal(t, color.Green, style.GetBackground(),
|
||||
"trailing cell at (%d, 2) should have green bg", x)
|
||||
ACTUAL: */
|
||||
assert.Equal(t, tcell.ColorDefault, style.GetBackground(),
|
||||
"trailing cell at (%d, 2) falls back to default bg", x)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue