detached acd4dbe

refactor(vt): export lineBoundary as LineBound

commit
acd4dbe4d78f17a4559a85db589bd4afa0e5665b
author
brice · · 2026-07-25 05:46 UTC
parent
c240dab
signature
GPG VerifiedCD88F9E564E0102646FB6077A1A07C7C7C3E349D · arch-gpg Verified for the committer email
3 files · +45 -43
M pkg/vt/buffer.go +34 -32
@@ -7,26 +7,28 @@ import "github.com/bnema/vev/pkg/renderer"
77 // reflow is bounded by the live grid.
88 type buffer struct {
99 frame renderer.Frame
10- boundaries []lineBoundary
10+ boundaries []LineBound
1111 }
1212
13-type lineBoundary struct {
14- // end is the meaningful cell extent. It excludes padding introduced when a
15- // wide rune was moved off the right edge.
16- end int
17- soft bool // the row continues into the following physical row
13+// LineBound describes a physical row's logical extent. End is exclusive: it is
14+// the count of meaningful cells, so the last significant column is End-1. It
15+// excludes padding introduced when a wide rune was moved off the right edge.
16+// Soft reports that the row continues into the following physical row.
17+type LineBound struct {
18+ End int
19+ Soft bool
1820 }
1921
2022 func newBuffer(width, height int) *buffer {
21- return &buffer{frame: renderer.NewFrame(width, height), boundaries: make([]lineBoundary, height)}
23+ return &buffer{frame: renderer.NewFrame(width, height), boundaries: make([]LineBound, height)}
2224 }
2325
2426 func bufferFromFrame(frame renderer.Frame) *buffer {
25- return &buffer{frame: frame, boundaries: make([]lineBoundary, frame.Height)}
27+ return &buffer{frame: frame, boundaries: make([]LineBound, frame.Height)}
2628 }
2729
2830 func (b *buffer) clone() *buffer {
29- out := &buffer{frame: cloneFrame(b.frame), boundaries: append([]lineBoundary(nil), b.boundaries...)}
31+ out := &buffer{frame: cloneFrame(b.frame), boundaries: append([]LineBound(nil), b.boundaries...)}
3032 return out
3133 }
3234
@@ -34,40 +36,40 @@ func (b *buffer) content(y, end int) {
3436 if y < 0 || y >= len(b.boundaries) {
3537 return
3638 }
37- b.boundaries[y].end = max(b.boundaries[y].end, clamp(end, 0, b.frame.Width))
39+ b.boundaries[y].End = max(b.boundaries[y].End, clamp(end, 0, b.frame.Width))
3840 }
3941
4042 func (b *buffer) truncate(y, end int) {
4143 if y < 0 || y >= len(b.boundaries) {
4244 return
4345 }
44- b.boundaries[y].end = min(b.boundaries[y].end, clamp(end, 0, b.frame.Width))
46+ b.boundaries[y].End = min(b.boundaries[y].End, clamp(end, 0, b.frame.Width))
4547 }
4648
4749 // insert retains the meaningful shifted tail when insertion happens within it.
4850 func (b *buffer) insert(y, at, width int) {
49- if y < 0 || y >= len(b.boundaries) || at >= b.boundaries[y].end {
51+ if y < 0 || y >= len(b.boundaries) || at >= b.boundaries[y].End {
5052 return
5153 }
52- b.boundaries[y].end = min(b.boundaries[y].end+width, b.frame.Width)
54+ b.boundaries[y].End = min(b.boundaries[y].End+width, b.frame.Width)
5355 }
5456
5557 func (b *buffer) hard(y int) {
5658 if y >= 0 && y < len(b.boundaries) {
57- b.boundaries[y].soft = false
59+ b.boundaries[y].Soft = false
5860 }
5961 }
6062
6163 func (b *buffer) soft(y int) {
6264 if y >= 0 && y < len(b.boundaries) {
63- b.boundaries[y].end = b.frame.Width
64- b.boundaries[y].soft = true
65+ b.boundaries[y].End = b.frame.Width
66+ b.boundaries[y].Soft = true
6567 }
6668 }
6769
6870 func (b *buffer) continueRow(y int) {
6971 if y >= 0 && y < len(b.boundaries) {
70- b.boundaries[y].soft = true
72+ b.boundaries[y].Soft = true
7173 }
7274 }
7375
@@ -80,10 +82,10 @@ func (b *buffer) clear(y, x0, x1 int) {
8082 // Erasing through the right edge leaves nothing flowing onto the next
8183 // row: the logical line ends here. Keeping a stale soft link would let
8284 // reflow merge a repainted row with the unrelated row below it.
83- b.boundaries[y].soft = false
85+ b.boundaries[y].Soft = false
8486 }
8587 if x0 == 0 && x1 >= b.frame.Width {
86- b.boundaries[y] = lineBoundary{}
88+ b.boundaries[y] = LineBound{}
8789 }
8890 }
8991
@@ -95,7 +97,7 @@ func (b *buffer) scrollUp(top, bottom, n int) {
9597 b.hard(top - 1)
9698 copy(b.boundaries[top:bottom-n+1], b.boundaries[top+n:bottom+1])
9799 for y := bottom - n + 1; y <= bottom; y++ {
98- b.boundaries[y] = lineBoundary{}
100+ b.boundaries[y] = LineBound{}
99101 }
100102 b.hard(bottom - n)
101103 }
@@ -106,19 +108,19 @@ func (b *buffer) scrollDown(top, bottom, n int) {
106108 b.hard(top - 1)
107109 copy(b.boundaries[top+n:bottom+1], b.boundaries[top:bottom-n+1])
108110 for y := top; y < top+n; y++ {
109- b.boundaries[y] = lineBoundary{}
111+ b.boundaries[y] = LineBound{}
110112 }
111113 b.hard(bottom)
112114 }
113115
114116 func (b *buffer) hydrate() {
115117 for y := range b.boundaries {
116- if b.boundaries[y].end != 0 {
118+ if b.boundaries[y].End != 0 {
117119 continue
118120 }
119121 for x := b.frame.Width - 1; x >= 0; x-- {
120122 if !b.frame.At(x, y).Equal(renderer.BlankCell()) {
121- b.boundaries[y].end = x + 1
123+ b.boundaries[y].End = x + 1
122124 break
123125 }
124126 }
@@ -129,7 +131,7 @@ type bufferCursor struct{ row, col int }
129131
130132 func (b *buffer) hasSoft() bool {
131133 for _, boundary := range b.boundaries {
132- if boundary.soft {
134+ if boundary.Soft {
133135 return true
134136 }
135137 }
@@ -153,7 +155,7 @@ func (b *buffer) resizeFixed(width, height int, active, saved *bufferCursor) [][
153155 // sequence does not add metadata allocation to every resize epoch.
154156 boundaries := b.boundaries
155157 if cap(boundaries) < height {
156- boundaries = make([]lineBoundary, height)
158+ boundaries = make([]LineBound, height)
157159 } else {
158160 boundaries = boundaries[:height]
159161 }
@@ -166,7 +168,7 @@ func (b *buffer) resizeFixed(width, height int, active, saved *bufferCursor) [][
166168 }
167169 copy(next.frame.Row(y), b.frame.Row(sy))
168170 next.boundaries[y] = b.boundaries[sy]
169- next.boundaries[y].end = min(next.boundaries[y].end, width)
171+ next.boundaries[y].End = min(next.boundaries[y].End, width)
170172 repairFrameRow(next.frame, y)
171173 copied++
172174 }
@@ -194,7 +196,7 @@ func (b *buffer) cursorReflowPoints(active, saved *bufferCursor) [2]reflowPoint
194196 cursors := [2]*bufferCursor{active, saved}
195197 for start := 0; start < b.frame.Height; {
196198 end := start
197- for b.boundaries[end].soft && end+1 < b.frame.Height {
199+ for b.boundaries[end].Soft && end+1 < b.frame.Height {
198200 end++
199201 }
200202 for i, cur := range cursors {
@@ -203,11 +205,11 @@ func (b *buffer) cursorReflowPoints(active, saved *bufferCursor) [2]reflowPoint
203205 }
204206 offset := 0
205207 for y := start; y < cur.row; y++ {
206- offset += b.boundaries[y].end
208+ offset += b.boundaries[y].End
207209 }
208210 points[i] = reflowPoint{
209211 line: start,
210- offset: offset + min(clamp(cur.col, 0, b.frame.Width), b.boundaries[cur.row].end),
212+ offset: offset + min(clamp(cur.col, 0, b.frame.Width), b.boundaries[cur.row].End),
211213 }
212214 }
213215 start = end + 1
@@ -287,7 +289,7 @@ func (b *buffer) layoutReflow(width int, points *[2]reflowPoint, dst *buffer, sh
287289 }
288290 finishRow := func(soft bool) {
289291 if dst != nil && row >= shift && row < shift+dst.frame.Height {
290- dst.boundaries[row-shift] = lineBoundary{end: col, soft: soft}
292+ dst.boundaries[row-shift] = LineBound{End: col, Soft: soft}
291293 }
292294 row++
293295 col = 0
@@ -312,7 +314,7 @@ func (b *buffer) layoutReflow(width int, points *[2]reflowPoint, dst *buffer, sh
312314
313315 for start := 0; start < b.frame.Height; {
314316 end := start
315- for b.boundaries[end].soft && end+1 < b.frame.Height {
317+ for b.boundaries[end].Soft && end+1 < b.frame.Height {
316318 end++
317319 }
318320 reflow := end > start
@@ -320,7 +322,7 @@ func (b *buffer) layoutReflow(width int, points *[2]reflowPoint, dst *buffer, sh
320322 truncated := false
321323 for y := start; y <= end && !truncated; y++ {
322324 cells := b.frame.Row(y)
323- limit := b.boundaries[y].end
325+ limit := b.boundaries[y].End
324326 for x := 0; x < limit; {
325327 cell := cells[x]
326328 if cell.Continuation { // Repair malformed rows by dropping orphaned tails.
@@ -7,26 +7,28 @@ import "github.com/bnema/vev/pkg/renderer"
7 // reflow is bounded by the live grid.7 // reflow is bounded by the live grid.
8 type buffer struct {8 type buffer struct {
9 frame renderer.Frame9 frame renderer.Frame
10- boundaries []lineBoundary10+ boundaries []LineBound
11 }11 }
12 12
13-type lineBoundary struct {13+// LineBound describes a physical row's logical extent. End is exclusive: it is
14- // end is the meaningful cell extent. It excludes padding introduced when a14+// the count of meaningful cells, so the last significant column is End-1. It
15- // wide rune was moved off the right edge.15+// excludes padding introduced when a wide rune was moved off the right edge.
16- end int16+// Soft reports that the row continues into the following physical row.
17- soft bool // the row continues into the following physical row17+type LineBound struct {
18+ End int
19+ Soft bool
18 }20 }
19 21
20 func newBuffer(width, height int) *buffer {22 func newBuffer(width, height int) *buffer {
21- return &buffer{frame: renderer.NewFrame(width, height), boundaries: make([]lineBoundary, height)}23+ return &buffer{frame: renderer.NewFrame(width, height), boundaries: make([]LineBound, height)}
22 }24 }
23 25
24 func bufferFromFrame(frame renderer.Frame) *buffer {26 func bufferFromFrame(frame renderer.Frame) *buffer {
25- return &buffer{frame: frame, boundaries: make([]lineBoundary, frame.Height)}27+ return &buffer{frame: frame, boundaries: make([]LineBound, frame.Height)}
26 }28 }
27 29
28 func (b *buffer) clone() *buffer {30 func (b *buffer) clone() *buffer {
29- out := &buffer{frame: cloneFrame(b.frame), boundaries: append([]lineBoundary(nil), b.boundaries...)}31+ out := &buffer{frame: cloneFrame(b.frame), boundaries: append([]LineBound(nil), b.boundaries...)}
30 return out32 return out
31 }33 }
32 34
@@ -34,40 +36,40 @@ func (b *buffer) content(y, end int) {
34 if y < 0 || y >= len(b.boundaries) {36 if y < 0 || y >= len(b.boundaries) {
35 return37 return
36 }38 }
37- b.boundaries[y].end = max(b.boundaries[y].end, clamp(end, 0, b.frame.Width))39+ b.boundaries[y].End = max(b.boundaries[y].End, clamp(end, 0, b.frame.Width))
38 }40 }
39 41
40 func (b *buffer) truncate(y, end int) {42 func (b *buffer) truncate(y, end int) {
41 if y < 0 || y >= len(b.boundaries) {43 if y < 0 || y >= len(b.boundaries) {
42 return44 return
43 }45 }
44- b.boundaries[y].end = min(b.boundaries[y].end, clamp(end, 0, b.frame.Width))46+ b.boundaries[y].End = min(b.boundaries[y].End, clamp(end, 0, b.frame.Width))
45 }47 }
46 48
47 // insert retains the meaningful shifted tail when insertion happens within it.49 // insert retains the meaningful shifted tail when insertion happens within it.
48 func (b *buffer) insert(y, at, width int) {50 func (b *buffer) insert(y, at, width int) {
49- if y < 0 || y >= len(b.boundaries) || at >= b.boundaries[y].end {51+ if y < 0 || y >= len(b.boundaries) || at >= b.boundaries[y].End {
50 return52 return
51 }53 }
52- b.boundaries[y].end = min(b.boundaries[y].end+width, b.frame.Width)54+ b.boundaries[y].End = min(b.boundaries[y].End+width, b.frame.Width)
53 }55 }
54 56
55 func (b *buffer) hard(y int) {57 func (b *buffer) hard(y int) {
56 if y >= 0 && y < len(b.boundaries) {58 if y >= 0 && y < len(b.boundaries) {
57- b.boundaries[y].soft = false59+ b.boundaries[y].Soft = false
58 }60 }
59 }61 }
60 62
61 func (b *buffer) soft(y int) {63 func (b *buffer) soft(y int) {
62 if y >= 0 && y < len(b.boundaries) {64 if y >= 0 && y < len(b.boundaries) {
63- b.boundaries[y].end = b.frame.Width65+ b.boundaries[y].End = b.frame.Width
64- b.boundaries[y].soft = true66+ b.boundaries[y].Soft = true
65 }67 }
66 }68 }
67 69
68 func (b *buffer) continueRow(y int) {70 func (b *buffer) continueRow(y int) {
69 if y >= 0 && y < len(b.boundaries) {71 if y >= 0 && y < len(b.boundaries) {
70- b.boundaries[y].soft = true72+ b.boundaries[y].Soft = true
71 }73 }
72 }74 }
73 75
@@ -80,10 +82,10 @@ func (b *buffer) clear(y, x0, x1 int) {
80 // Erasing through the right edge leaves nothing flowing onto the next82 // Erasing through the right edge leaves nothing flowing onto the next
81 // row: the logical line ends here. Keeping a stale soft link would let83 // row: the logical line ends here. Keeping a stale soft link would let
82 // reflow merge a repainted row with the unrelated row below it.84 // reflow merge a repainted row with the unrelated row below it.
83- b.boundaries[y].soft = false85+ b.boundaries[y].Soft = false
84 }86 }
85 if x0 == 0 && x1 >= b.frame.Width {87 if x0 == 0 && x1 >= b.frame.Width {
86- b.boundaries[y] = lineBoundary{}88+ b.boundaries[y] = LineBound{}
87 }89 }
88 }90 }
89 91
@@ -95,7 +97,7 @@ func (b *buffer) scrollUp(top, bottom, n int) {
95 b.hard(top - 1)97 b.hard(top - 1)
96 copy(b.boundaries[top:bottom-n+1], b.boundaries[top+n:bottom+1])98 copy(b.boundaries[top:bottom-n+1], b.boundaries[top+n:bottom+1])
97 for y := bottom - n + 1; y <= bottom; y++ {99 for y := bottom - n + 1; y <= bottom; y++ {
98- b.boundaries[y] = lineBoundary{}100+ b.boundaries[y] = LineBound{}
99 }101 }
100 b.hard(bottom - n)102 b.hard(bottom - n)
101 }103 }
@@ -106,19 +108,19 @@ func (b *buffer) scrollDown(top, bottom, n int) {
106 b.hard(top - 1)108 b.hard(top - 1)
107 copy(b.boundaries[top+n:bottom+1], b.boundaries[top:bottom-n+1])109 copy(b.boundaries[top+n:bottom+1], b.boundaries[top:bottom-n+1])
108 for y := top; y < top+n; y++ {110 for y := top; y < top+n; y++ {
109- b.boundaries[y] = lineBoundary{}111+ b.boundaries[y] = LineBound{}
110 }112 }
111 b.hard(bottom)113 b.hard(bottom)
112 }114 }
113 115
114 func (b *buffer) hydrate() {116 func (b *buffer) hydrate() {
115 for y := range b.boundaries {117 for y := range b.boundaries {
116- if b.boundaries[y].end != 0 {118+ if b.boundaries[y].End != 0 {
117 continue119 continue
118 }120 }
119 for x := b.frame.Width - 1; x >= 0; x-- {121 for x := b.frame.Width - 1; x >= 0; x-- {
120 if !b.frame.At(x, y).Equal(renderer.BlankCell()) {122 if !b.frame.At(x, y).Equal(renderer.BlankCell()) {
121- b.boundaries[y].end = x + 1123+ b.boundaries[y].End = x + 1
122 break124 break
123 }125 }
124 }126 }
@@ -129,7 +131,7 @@ type bufferCursor struct{ row, col int }
129 131
130 func (b *buffer) hasSoft() bool {132 func (b *buffer) hasSoft() bool {
131 for _, boundary := range b.boundaries {133 for _, boundary := range b.boundaries {
132- if boundary.soft {134+ if boundary.Soft {
133 return true135 return true
134 }136 }
135 }137 }
@@ -153,7 +155,7 @@ func (b *buffer) resizeFixed(width, height int, active, saved *bufferCursor) [][
153 // sequence does not add metadata allocation to every resize epoch.155 // sequence does not add metadata allocation to every resize epoch.
154 boundaries := b.boundaries156 boundaries := b.boundaries
155 if cap(boundaries) < height {157 if cap(boundaries) < height {
156- boundaries = make([]lineBoundary, height)158+ boundaries = make([]LineBound, height)
157 } else {159 } else {
158 boundaries = boundaries[:height]160 boundaries = boundaries[:height]
159 }161 }
@@ -166,7 +168,7 @@ func (b *buffer) resizeFixed(width, height int, active, saved *bufferCursor) [][
166 }168 }
167 copy(next.frame.Row(y), b.frame.Row(sy))169 copy(next.frame.Row(y), b.frame.Row(sy))
168 next.boundaries[y] = b.boundaries[sy]170 next.boundaries[y] = b.boundaries[sy]
169- next.boundaries[y].end = min(next.boundaries[y].end, width)171+ next.boundaries[y].End = min(next.boundaries[y].End, width)
170 repairFrameRow(next.frame, y)172 repairFrameRow(next.frame, y)
171 copied++173 copied++
172 }174 }
@@ -194,7 +196,7 @@ func (b *buffer) cursorReflowPoints(active, saved *bufferCursor) [2]reflowPoint
194 cursors := [2]*bufferCursor{active, saved}196 cursors := [2]*bufferCursor{active, saved}
195 for start := 0; start < b.frame.Height; {197 for start := 0; start < b.frame.Height; {
196 end := start198 end := start
197- for b.boundaries[end].soft && end+1 < b.frame.Height {199+ for b.boundaries[end].Soft && end+1 < b.frame.Height {
198 end++200 end++
199 }201 }
200 for i, cur := range cursors {202 for i, cur := range cursors {
@@ -203,11 +205,11 @@ func (b *buffer) cursorReflowPoints(active, saved *bufferCursor) [2]reflowPoint
203 }205 }
204 offset := 0206 offset := 0
205 for y := start; y < cur.row; y++ {207 for y := start; y < cur.row; y++ {
206- offset += b.boundaries[y].end208+ offset += b.boundaries[y].End
207 }209 }
208 points[i] = reflowPoint{210 points[i] = reflowPoint{
209 line: start,211 line: start,
210- offset: offset + min(clamp(cur.col, 0, b.frame.Width), b.boundaries[cur.row].end),212+ offset: offset + min(clamp(cur.col, 0, b.frame.Width), b.boundaries[cur.row].End),
211 }213 }
212 }214 }
213 start = end + 1215 start = end + 1
@@ -287,7 +289,7 @@ func (b *buffer) layoutReflow(width int, points *[2]reflowPoint, dst *buffer, sh
287 }289 }
288 finishRow := func(soft bool) {290 finishRow := func(soft bool) {
289 if dst != nil && row >= shift && row < shift+dst.frame.Height {291 if dst != nil && row >= shift && row < shift+dst.frame.Height {
290- dst.boundaries[row-shift] = lineBoundary{end: col, soft: soft}292+ dst.boundaries[row-shift] = LineBound{End: col, Soft: soft}
291 }293 }
292 row++294 row++
293 col = 0295 col = 0
@@ -312,7 +314,7 @@ func (b *buffer) layoutReflow(width int, points *[2]reflowPoint, dst *buffer, sh
312 314
313 for start := 0; start < b.frame.Height; {315 for start := 0; start < b.frame.Height; {
314 end := start316 end := start
315- for b.boundaries[end].soft && end+1 < b.frame.Height {317+ for b.boundaries[end].Soft && end+1 < b.frame.Height {
316 end++318 end++
317 }319 }
318 reflow := end > start320 reflow := end > start
@@ -320,7 +322,7 @@ func (b *buffer) layoutReflow(width int, points *[2]reflowPoint, dst *buffer, sh
320 truncated := false322 truncated := false
321 for y := start; y <= end && !truncated; y++ {323 for y := start; y <= end && !truncated; y++ {
322 cells := b.frame.Row(y)324 cells := b.frame.Row(y)
323- limit := b.boundaries[y].end325+ limit := b.boundaries[y].End
324 for x := 0; x < limit; {326 for x := 0; x < limit; {
325 cell := cells[x]327 cell := cells[x]
326 if cell.Continuation { // Repair malformed rows by dropping orphaned tails.328 if cell.Continuation { // Repair malformed rows by dropping orphaned tails.
M pkg/vt/screen_csi.go +1 -1
@@ -409,7 +409,7 @@ func (s *Screen) clearScreenMode(mode int) {
409409 s.Frame.Cells[i] = blank
410410 }
411411 for y := range s.buffer.boundaries {
412- s.buffer.boundaries[y] = lineBoundary{soft: s.buffer.boundaries[y].soft}
412+ s.buffer.boundaries[y] = LineBound{Soft: s.buffer.boundaries[y].Soft}
413413 }
414414 s.record(renderer.Damage{Kind: renderer.DamageClear, X: 0, Y: 0, Width: s.Frame.Width, Height: s.Frame.Height, Count: 1})
415415 default:
@@ -409,7 +409,7 @@ func (s *Screen) clearScreenMode(mode int) {
409 s.Frame.Cells[i] = blank409 s.Frame.Cells[i] = blank
410 }410 }
411 for y := range s.buffer.boundaries {411 for y := range s.buffer.boundaries {
412- s.buffer.boundaries[y] = lineBoundary{soft: s.buffer.boundaries[y].soft}412+ s.buffer.boundaries[y] = LineBound{Soft: s.buffer.boundaries[y].Soft}
413 }413 }
414 s.record(renderer.Damage{Kind: renderer.DamageClear, X: 0, Y: 0, Width: s.Frame.Width, Height: s.Frame.Height, Count: 1})414 s.record(renderer.Damage{Kind: renderer.DamageClear, X: 0, Y: 0, Width: s.Frame.Width, Height: s.Frame.Height, Count: 1})
415 default:415 default:
M pkg/vt/snapshot.go +10 -10
@@ -129,11 +129,11 @@ func MarshalVisible(frame renderer.Frame) ([]byte, error) {
129129 if _, _, _, ok := visibleEncodingBudget(frame.Width, frame.Height); !ok {
130130 return nil, fmt.Errorf("marshal visible: %w", errInvalidHistory)
131131 }
132- boundaries := make([]lineBoundary, frame.Height)
132+ boundaries := make([]LineBound, frame.Height)
133133 for y := range boundaries {
134134 for x := frame.Width - 1; x >= 0; x-- {
135135 if !frame.At(x, y).Equal(renderer.BlankCell()) {
136- boundaries[y].end = x + 1
136+ boundaries[y].End = x + 1
137137 break
138138 }
139139 }
@@ -146,7 +146,7 @@ func MarshalVisible(frame renderer.Frame) ([]byte, error) {
146146 // after releasing that lock.
147147 type PrimaryVisibleSnapshot struct {
148148 frame renderer.Frame
149- boundaries []lineBoundary
149+ boundaries []LineBound
150150 }
151151
152152 // PrimaryVisibleSnapshot copies the saved primary frame and its logical-line
@@ -162,7 +162,7 @@ func (s *Screen) PrimaryVisibleSnapshot() PrimaryVisibleSnapshot {
162162 }
163163 return PrimaryVisibleSnapshot{
164164 frame: b.frame.Clone(),
165- boundaries: append([]lineBoundary(nil), b.boundaries...),
165+ boundaries: append([]LineBound(nil), b.boundaries...),
166166 }
167167 }
168168
@@ -184,7 +184,7 @@ func (s *Screen) MarshalPrimaryVisible() ([]byte, error) {
184184 return s.PrimaryVisibleSnapshot().Marshal()
185185 }
186186
187-func marshalVisible(frame renderer.Frame, boundaries []lineBoundary) ([]byte, error) {
187+func marshalVisible(frame renderer.Frame, boundaries []LineBound) ([]byte, error) {
188188 _, bytes, boundaryBytes, ok := visibleEncodingBudget(frame.Width, frame.Height)
189189 if !ok || len(boundaries) != frame.Height {
190190 return nil, fmt.Errorf("marshal visible: %w", errInvalidHistory)
@@ -203,8 +203,8 @@ func marshalVisible(frame renderer.Frame, boundaries []lineBoundary) ([]byte, er
203203 }
204204 }
205205 for _, boundary := range boundaries {
206- out = binary.BigEndian.AppendUint32(out, uint32(boundary.end))
207- if boundary.soft {
206+ out = binary.BigEndian.AppendUint32(out, uint32(boundary.End))
207+ if boundary.Soft {
208208 out = append(out, 1)
209209 } else {
210210 out = append(out, 0)
@@ -269,7 +269,7 @@ func UnmarshalVisible(data []byte) (renderer.Frame, error) {
269269 return frame, err
270270 }
271271
272-func unmarshalVisible(data []byte) (renderer.Frame, []lineBoundary, error) {
272+func unmarshalVisible(data []byte) (renderer.Frame, []LineBound, error) {
273273 if _, err := PreflightVisibleBlob(data); err != nil {
274274 return renderer.Frame{}, nil, err
275275 }
@@ -283,10 +283,10 @@ func unmarshalVisible(data []byte) (renderer.Frame, []lineBoundary, error) {
283283 frame.Set(x, y, cell)
284284 }
285285 }
286- boundaries := make([]lineBoundary, height)
286+ boundaries := make([]LineBound, height)
287287 offset := 13 + int(cellBytes)
288288 for y := range boundaries {
289- boundaries[y] = lineBoundary{end: clamp(int(binary.BigEndian.Uint32(data[offset:offset+4])), 0, width), soft: data[offset+4] == 1}
289+ boundaries[y] = LineBound{End: clamp(int(binary.BigEndian.Uint32(data[offset:offset+4])), 0, width), Soft: data[offset+4] == 1}
290290 offset += 5
291291 }
292292 return frame, boundaries, nil
@@ -129,11 +129,11 @@ func MarshalVisible(frame renderer.Frame) ([]byte, error) {
129 if _, _, _, ok := visibleEncodingBudget(frame.Width, frame.Height); !ok {129 if _, _, _, ok := visibleEncodingBudget(frame.Width, frame.Height); !ok {
130 return nil, fmt.Errorf("marshal visible: %w", errInvalidHistory)130 return nil, fmt.Errorf("marshal visible: %w", errInvalidHistory)
131 }131 }
132- boundaries := make([]lineBoundary, frame.Height)132+ boundaries := make([]LineBound, frame.Height)
133 for y := range boundaries {133 for y := range boundaries {
134 for x := frame.Width - 1; x >= 0; x-- {134 for x := frame.Width - 1; x >= 0; x-- {
135 if !frame.At(x, y).Equal(renderer.BlankCell()) {135 if !frame.At(x, y).Equal(renderer.BlankCell()) {
136- boundaries[y].end = x + 1136+ boundaries[y].End = x + 1
137 break137 break
138 }138 }
139 }139 }
@@ -146,7 +146,7 @@ func MarshalVisible(frame renderer.Frame) ([]byte, error) {
146 // after releasing that lock.146 // after releasing that lock.
147 type PrimaryVisibleSnapshot struct {147 type PrimaryVisibleSnapshot struct {
148 frame renderer.Frame148 frame renderer.Frame
149- boundaries []lineBoundary149+ boundaries []LineBound
150 }150 }
151 151
152 // PrimaryVisibleSnapshot copies the saved primary frame and its logical-line152 // PrimaryVisibleSnapshot copies the saved primary frame and its logical-line
@@ -162,7 +162,7 @@ func (s *Screen) PrimaryVisibleSnapshot() PrimaryVisibleSnapshot {
162 }162 }
163 return PrimaryVisibleSnapshot{163 return PrimaryVisibleSnapshot{
164 frame: b.frame.Clone(),164 frame: b.frame.Clone(),
165- boundaries: append([]lineBoundary(nil), b.boundaries...),165+ boundaries: append([]LineBound(nil), b.boundaries...),
166 }166 }
167 }167 }
168 168
@@ -184,7 +184,7 @@ func (s *Screen) MarshalPrimaryVisible() ([]byte, error) {
184 return s.PrimaryVisibleSnapshot().Marshal()184 return s.PrimaryVisibleSnapshot().Marshal()
185 }185 }
186 186
187-func marshalVisible(frame renderer.Frame, boundaries []lineBoundary) ([]byte, error) {187+func marshalVisible(frame renderer.Frame, boundaries []LineBound) ([]byte, error) {
188 _, bytes, boundaryBytes, ok := visibleEncodingBudget(frame.Width, frame.Height)188 _, bytes, boundaryBytes, ok := visibleEncodingBudget(frame.Width, frame.Height)
189 if !ok || len(boundaries) != frame.Height {189 if !ok || len(boundaries) != frame.Height {
190 return nil, fmt.Errorf("marshal visible: %w", errInvalidHistory)190 return nil, fmt.Errorf("marshal visible: %w", errInvalidHistory)
@@ -203,8 +203,8 @@ func marshalVisible(frame renderer.Frame, boundaries []lineBoundary) ([]byte, er
203 }203 }
204 }204 }
205 for _, boundary := range boundaries {205 for _, boundary := range boundaries {
206- out = binary.BigEndian.AppendUint32(out, uint32(boundary.end))206+ out = binary.BigEndian.AppendUint32(out, uint32(boundary.End))
207- if boundary.soft {207+ if boundary.Soft {
208 out = append(out, 1)208 out = append(out, 1)
209 } else {209 } else {
210 out = append(out, 0)210 out = append(out, 0)
@@ -269,7 +269,7 @@ func UnmarshalVisible(data []byte) (renderer.Frame, error) {
269 return frame, err269 return frame, err
270 }270 }
271 271
272-func unmarshalVisible(data []byte) (renderer.Frame, []lineBoundary, error) {272+func unmarshalVisible(data []byte) (renderer.Frame, []LineBound, error) {
273 if _, err := PreflightVisibleBlob(data); err != nil {273 if _, err := PreflightVisibleBlob(data); err != nil {
274 return renderer.Frame{}, nil, err274 return renderer.Frame{}, nil, err
275 }275 }
@@ -283,10 +283,10 @@ func unmarshalVisible(data []byte) (renderer.Frame, []lineBoundary, error) {
283 frame.Set(x, y, cell)283 frame.Set(x, y, cell)
284 }284 }
285 }285 }
286- boundaries := make([]lineBoundary, height)286+ boundaries := make([]LineBound, height)
287 offset := 13 + int(cellBytes)287 offset := 13 + int(cellBytes)
288 for y := range boundaries {288 for y := range boundaries {
289- boundaries[y] = lineBoundary{end: clamp(int(binary.BigEndian.Uint32(data[offset:offset+4])), 0, width), soft: data[offset+4] == 1}289+ boundaries[y] = LineBound{End: clamp(int(binary.BigEndian.Uint32(data[offset:offset+4])), 0, width), Soft: data[offset+4] == 1}
290 offset += 5290 offset += 5
291 }291 }
292 return frame, boundaries, nil292 return frame, boundaries, nil

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 12

Tags 11

↑↓ moveenter openesc close