tsvsheet logo tsvsheet

Hide, header and freeze

A sheet can say what a viewport should do with it — which rows and columns to hide, which carry headers, which stay pinned while the rest scrolls — declared in the file itself, and never at the cost of the data.

A spreadsheet has a view as well as data: the scratch column you would rather not look at, the row that labels everything below it, the header you want pinned while you scroll. In tsvsheet that view is declared in the file, on #. lines, so it travels with the sheet — through git, through a shared link, into every editor.

It never costs you the data. Delete every directive and the file is byte-identical, and computes identically. Nothing a formula can read changes:

$ diff <(tsv render view-directives.tsvt) <(grep -v '^#\.' view-directives.tsvt | tsv render)
$

Three keys

text
#.hide	cols(range(B:M))
#.header	rows(count(1))
#.freeze	rows(count(2), count(-1))

hide keeps rows or columns out of sight, header names the ones that label the data, and freeze pins them while the rest scrolls. The axis lives in the value — rows(…) or cols(…) — so there are three keys, one for each thing a viewport does.

Two ways to name rows and columns

Every item is written out, because a bare number reads two ways: is rows(3) row 3, or three rows?

You writeYou get
range(20:31)rows 20 through 31
range(20:31, 40:40)two spans at once
range(20:-1)row 20 to the bottom — a negative index counts from the end
count(3)the first three
count(-1)the last one, whatever it turns out to be

They are not two spellings of the same thing. Insert a row inside a three-row header and count(3) becomes count(4) — the new row joins the header. Write it as range(1:3) and the block shifts down to range(2:4) instead, leaving the new row as data. Both are useful; pick the one that means what you meant.

Columns work the same way with letters: cols(range(B:M)), cols(count(1)), cols(count(-1)) for the last column.

What each surface does with it

A viewport honours all three. A pipeline has no viewport, so it honours what it can and ignores the rest — and it never drops your data on the floor:

$ tsv render view-directives.tsvt          # hidden columns still written out
Region	Units	Revenue	Unit price (scratch)
North	120	510	4.25

$ tsv render --hidden=drop view-directives.tsvt   # the projected artifact, on request
Region	Units	Revenue
North	120	510

tsv render --format html writes declared header rows as <th> inside a <thead>. In tsv serve and tsv tui the hidden rows and columns are simply not drawn — with a Show hidden button in the browser and v in the terminal, which reveals without unhiding: the file is untouched. In a ```sheet fence on a docs site, hidden rows are left out of the table and the source pane still carries the whole file.

Two things it will not do

Row numbers never shift. Row 7 is row 7 whether or not rows 3–6 are hidden, because every formula still addresses them; the gutter marks the skip rather than closing it up.

And the language stops here on purpose. There are no colors, fonts, alignments or number formats — presentation inside the grid is what breaks a plain-text spreadsheet’s one promise, that it diffs line by line and means the same thing to cat, git and every tool you own. A view directive says what a viewport does with your data. It never says how your data looks.