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/wasm-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,
})
);Line numbers correspond to HTML's data-line attribute and the numbers
accepted by highlight_lines.
What each formatter renders
| Formatter | Gutter |
|---|---|
| HTML Inline | <span class="l-line-number"> inside the line's <span> |
| 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>:
<span class="l-line" data-line="1"><span class="l-line-number" style="-webkit-user-select: none; user-select: none; color: #51576d;" aria-hidden="true">1</span><span style="color: #ca9ee6;">fn</span></span>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. All three keep the
gutter out of selection copy with user-select: none (and its WebKit prefix).
Theme CSS does not choose a gutter width, so give it a column:
.l-line-number {
display: inline-block;
width: 3ch;
margin-right: 1ch;
text-align: right;
}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.
With line_numbers disabled, HTML contains no gutter elements. Each line has a
data-line attribute, which you can use to display numbers with CSS:
.l-line::before { content: attr(data-line); }innerText and textContent include the gutter's text; user-select affects
selection only. For a copy button, use the original source rather than
either property. That also preserves CRLF and final newlines exactly. Chromium
may omit trailing blank rows from selection copy when they contain only gutters.
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.
A final newline terminates the last line; it does not add an extra line number in either terminal or HTML output.
lumis highlight -f terminal -t catppuccin_frappe -n main.rs 8 fn main() {
9 println!("hello");
10 }