Line Numbers
Number the lines Lumis renders, in HTML and in the terminal.
line_numbers numbers the lines a formatter renders. The three HTML formatters
and terminal take it; bbcode_scoped has nothing to render a number into and
does not.
import {highlight} from '@lumis-sh/lumis';
import {htmlInline} from '@lumis-sh/lumis/formatters';
import javascript from '@lumis-sh/lumis/langs/javascript';
import frappe from '@lumis-sh/themes/catppuccin_frappe';
const html = await highlight(
'line 1\nline 2\nline 3',
htmlInline({language: javascript, theme: frappe, lineNumbers: true})
);
The number is the one every line already carries: HTML's data-line and
highlight_lines name the same line.
What each formatter renders
| Formatter | Gutter |
|---|---|
| HTML Inline | <span class="l-line-number"> inside the line's <div> |
| HTML Linked | the same |
| HTML Multi-Themes | the same |
| Terminal | the number, right-aligned to the widest one and followed by a space |
| BBCode Scoped | not available |
HTML
The gutter is the first child of the line, inside <code>:
<div class="l-line" data-line="1">
<span class="l-line-number" style="color: #51576d;" aria-hidden="true">1</span>
<span style="color: #ca9ee6;">fn</span>
</div>
It is a real element rather than content: attr(data-line) so the number is in
the document a reader can inspect, and aria-hidden so a screen reader is not
read a number before every line. Its appearance comes from the theme's LineNr
style. On a highlighted line it also has the l-line-number-highlighted class
and uses CursorLineNr, falling back to LineNr when that style is absent.
HTML Inline writes the style directly, HTML Multi-Themes writes the corresponding
theme variables, and HTML Linked uses the .l-line-number and
.l-line-number-highlighted rules in generated theme CSS. Theme CSS does not
lay out the gutter, so give it a column and keep it out of a copied selection:
.l-line-number {
display: inline-block;
width: 3ch;
margin-right: 1ch;
text-align: right;
user-select: none;
}
Those are layout properties, which theme CSS does not set, so they apply as
written. Recoloring the gutter is different. Theme CSS writes
.l-line-number:not(.l-line-number-highlighted) to keep LineNr off a
CursorLineNr gutter, and two classes beat the one in a bare .l-line-number,
which therefore loses however late it is loaded. Scope the rule under the block
instead, and load it after the theme stylesheet:
.lumis .l-line-number {
color: #888;
}
That is two classes as well, so it wins on order rather than specificity, and it
recolors the highlighted gutter along with the rest. To recolor only the lines
that are not highlighted, and to win regardless of order, add the same :not()
the theme rule carries.
Without line_numbers, nothing is added: data-line is on every line as it
always has been, and the CSS-only approach still works.
.l-line::before { content: attr(data-line); }
Terminal
The number is padded to the widest one the render will show, so the code stays aligned past line 9.
Neovim draws the number column with LineNr, and with CursorLineNr on the
cursor line, so CursorLine never reaches it. Lumis follows that: a highlighted
line's background starts at its text, not at its gutter. The gutter uses every
text attribute in the corresponding theme style, including foreground, bold,
italic, and decorations. When a theme has no CursorLineNr style, a highlighted
gutter falls back to its LineNr style.
The number is written with the line's text, so the empty line a trailing newline
opens carries none: a terminal writes nothing for that line, and a bare number
after the output would be all you saw of it. HTML numbers it, because HTML emits
a <div> for it.
lumis highlight -f terminal -t catppuccin_frappe -n main.rs
8 fn main() {
9 println!("hello");
10 }