v0.7.1 ff50ee4

history.md RawHistoryFolder

Working with history

Back to the README

History contains the lines that have scrolled off the visible screen. Each Screen has its own history; the library does not share one limit across panes or sessions.

Choose limits

Your application chooses the budget and passes it in HistoryConfig. vev-vt provides no default byte or line budget. It only measures the stored history and removes the oldest lines when a supplied limit is reached. The PTY transports terminal bytes; the application's history policy is separate.

Setting Meaning
MaxBytes Maximum uncompressed history data. Zero omits this ceiling. This is not a process-RSS limit.
MaxRows Maximum lines. Set to zero with positive MaxBytes for a byte-only limit.
Both limits zero Disable history.
Positive MaxRows, zero MaxBytes Limit only the number of lines. No byte budget is invented.
ChunkRows Internal grouping size, 1–256. Usually leave it at zero for the default, 256.

The visible primary and alternate screens do not count toward these limits. Grouping rows does not grant extra space beyond the limit.

If settings come from user input, call config.Validate() before creating a screen. Invalid settings make constructors panic. To change an existing history, call history.SetLimits(config): it returns an error for invalid input and removes excess old rows immediately after a valid reduction.

Use history.Limits() to read the effective settings, Len() for the number of lines and LogicalBytes() for the amount charged to the byte limit.

Read without exposing writable storage

  • history.View() captures the current contents. Later writes cannot change it.
  • view.Row(y) returns a copy of a line.
  • view.Cell(x, y) reads one cell; RowWidth(y) gets its width without copying it.
  • view.CopyRow(y, destination) reuses a slice supplied by your application.
  • view.Range(callback) walks lines in order. Each callback receives its own row. Check its returned error: a corrupt compressed page stops iteration rather than silently yielding incomplete data.

Older groups of rows, called chunks, can be shared between views. Their identity stays stable, which lets applications avoid saving or processing them repeatedly.

Save and restore

For a single history blob, use MarshalHistory and UnmarshalHistory. For incremental snapshots, use SnapshotView, MarshalHistoryChunk and MarshalHistoryTail; restore them with HistoryFromBlobs.

A snapshot includes row identities and line-wrap information as well as cells. The copied snapshot tail is reusable: asking for Tail() repeatedly does not copy it again.

The format is VTC1. Older VTH3 data is rejected, not migrated. Check decode errors and do not overwrite existing saved data after a failed restore. Application snapshot envelopes such as vev's VEVS format are outside this library.

Each blob is limited to 12,000 rows and 1,920,000 cells. Exceptional payload records and strings have a separate 64 MiB limit. Chunk-by-chunk persistence avoids putting an entire large history in one blob. The decoder checks the complete blob before allocating decoded grids, including references, duplicate IDs, string limits, truncation and extra trailing bytes.

Compress during idle time

Compression is optional. From the same goroutine or lock that owns the history, call history.CompressIdle(1) on an idle timer to visit at most one old page per call. The library does not start a timer or goroutine for you.

A page is compressed only after two visits with no read between them. The newest sealed page and the unfinished tail stay uncompressed. Reading an older page restores it automatically. This changes memory use, not how many lines are kept.

Restored pages are cached until later idle visits. Searches through Range and history serialization restore pages temporarily instead of caching every page. Use CompressionStats() to inspect compressed bytes, cold pages and restores. Its resident-byte estimate excludes Go allocator and map overhead.

Corrupted compressed data is never replaced with blank text. To handle errors before reading, call chunk.Restore() and check for ErrHistoryCorrupt. An ordinary read panics with that error if the library's private backing is corrupt. Restore checks length, checksum, page size and the usual VTC1 validation rules.

How byte accounting works

These implementation details matter when interpreting LogicalBytes():

Item Charged size
Stored cell 16 bytes
Row descriptor 4 bytes
Distinct style in a page, including default 32 bytes
Distinct nonempty exceptional payload 16 bytes plus its UTF-8 strings

Styles and exceptional values are shared within a page. Compression does not change these charges. The wire encoding is separate: ordinary cells use 9 bytes, and cells with an exceptional payload add a 4-byte reference. Only semantic values are serialized, never raw Go page memory or internal handles.

Benchmarks and optimization choices

source
1# Working with history
2
3[Back to the README](../README.md)
4
5History contains the lines that have scrolled off the visible screen. Each
6`Screen` has its own history; the library does not share one limit across panes
7or sessions.
8
9## Choose limits
10
11Your application chooses the budget and passes it in `HistoryConfig`.
12vev-vt provides no default byte or line budget. It only measures the stored
13history and removes the oldest lines when a supplied limit is reached.
14The PTY transports terminal bytes; the application's history policy is separate.
15
16| Setting | Meaning |
17| --- | --- |
18| `MaxBytes` | Maximum uncompressed history data. Zero omits this ceiling. This is not a process-RSS limit. |
19| `MaxRows` | Maximum lines. Set to zero with positive `MaxBytes` for a byte-only limit. |
20| Both limits zero | Disable history. |
21| Positive `MaxRows`, zero `MaxBytes` | Limit only the number of lines. No byte budget is invented. |
22| `ChunkRows` | Internal grouping size, 1–256. Usually leave it at zero for the default, 256. |
23
24The visible primary and alternate screens do not count toward these limits.
25Grouping rows does not grant extra space beyond the limit.
26
27If settings come from user input, call `config.Validate()` before creating a
28screen. Invalid settings make constructors panic. To change an existing history,
29call `history.SetLimits(config)`: it returns an error for invalid input and
30removes excess old rows immediately after a valid reduction.
31
32Use `history.Limits()` to read the effective settings, `Len()` for the number of
33lines and `LogicalBytes()` for the amount charged to the byte limit.
34
35## Read without exposing writable storage
36
37- `history.View()` captures the current contents. Later writes cannot change it.
38- `view.Row(y)` returns a copy of a line.
39- `view.Cell(x, y)` reads one cell; `RowWidth(y)` gets its width without copying it.
40- `view.CopyRow(y, destination)` reuses a slice supplied by your application.
41- `view.Range(callback)` walks lines in order. Each callback receives its own row.
42  Check its returned error: a corrupt compressed page stops iteration rather
43  than silently yielding incomplete data.
44
45Older groups of rows, called chunks, can be shared between views. Their identity
46stays stable, which lets applications avoid saving or processing them repeatedly.
47
48## Save and restore
49
50For a single history blob, use `MarshalHistory` and `UnmarshalHistory`.
51For incremental snapshots, use `SnapshotView`, `MarshalHistoryChunk` and
52`MarshalHistoryTail`; restore them with `HistoryFromBlobs`.
53
54A snapshot includes row identities and line-wrap information as well as cells.
55The copied snapshot tail is reusable: asking for `Tail()` repeatedly does not
56copy it again.
57
58The format is **VTC1**. Older VTH3 data is rejected, not migrated. Check decode
59errors and do not overwrite existing saved data after a failed restore. Application
60snapshot envelopes such as vev's VEVS format are outside this library.
61
62Each blob is limited to 12,000 rows and 1,920,000 cells. Exceptional payload
63records and strings have a separate 64 MiB limit. Chunk-by-chunk persistence
64avoids putting an entire large history in one blob. The decoder checks the
65complete blob before allocating decoded grids, including references, duplicate
66IDs, string limits, truncation and extra trailing bytes.
67
68## Compress during idle time
69
70Compression is optional. From the same goroutine or lock that owns the history,
71call `history.CompressIdle(1)` on an idle timer to visit at most one old page per
72call. The library does not start a timer or goroutine for you.
73
74A page is compressed only after two visits with no read between them. The newest
75sealed page and the unfinished tail stay uncompressed. Reading an older page
76restores it automatically. This changes memory use, not how many lines are kept.
77
78Restored pages are cached until later idle visits. Searches through `Range` and
79history serialization restore pages temporarily instead of caching every page.
80Use `CompressionStats()` to inspect compressed bytes, cold pages and restores.
81Its resident-byte estimate excludes Go allocator and map overhead.
82
83Corrupted compressed data is never replaced with blank text. To handle errors
84before reading, call `chunk.Restore()` and check for `ErrHistoryCorrupt`. An
85ordinary read panics with that error if the library's private backing is corrupt.
86Restore checks length, checksum, page size and the usual VTC1 validation rules.
87
88## How byte accounting works
89
90These implementation details matter when interpreting `LogicalBytes()`:
91
92| Item | Charged size |
93| --- | ---: |
94| Stored cell | 16 bytes |
95| Row descriptor | 4 bytes |
96| Distinct style in a page, including default | 32 bytes |
97| Distinct nonempty exceptional payload | 16 bytes plus its UTF-8 strings |
98
99Styles and exceptional values are shared within a page. Compression does not
100change these charges. The wire encoding is separate: ordinary cells use 9 bytes,
101and cells with an exceptional payload add a 4-byte reference. Only semantic
102values are serialized, never raw Go page memory or internal handles.
103
104[Benchmarks and optimization choices](storage-optimization.md)

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 2

Tags 13

↑↓ moveenter openesc close