highlighter

Tokens

When you render to something other than a web page — a PDF, a terminal, a GUI — take the tokens directly instead of producing HTML and parsing it back…

When you render to something other than a web page — a PDF, a terminal, a GUI — take the tokens directly instead of producing HTML and parsing it back apart. tokens returns the same information highlight uses, structured rather than serialized.

Tokenizing

tokens(code) returns one list of tokens per source line, with adjacent same-scope tokens merged:

val Right(hl) = Highlighter.fromJson(grammar): @unchecked

for line <- hl.tokens("val x = 42\n// note") do
  for tok <- line do
    render(tok.text, hl.categoryOf(tok))

Each Token is:

case class Token(text: String, scopes: List[String])

The line grouping is preserved: tokens(code).length is the number of lines, and concatenating a line’s token texts reproduces that line exactly — so you control line breaks and indentation yourself.

Categories and colours

A token’s effective scope is its innermost (last) one. categoryOf collapses it to one of the nine rendering categories — the same mapping the HTML back ends use — so you can drive your own palette:

def colour(tok: Token): MyColour =
  hl.categoryOf(tok) match           // keyword | string | comment | number | type |
    case "keyword" => myKeyword      // function | variable | operator | punctuation | ""
    case "string"  => myString
    case "comment" => myComment
    case "number"  => myNumber
    case _         => myDefault

An empty category ("") means the token carries no colour of its own — plain source text. category(scope) is the same mapping applied to a raw scope string, if you have one in hand.

Pairing with a theme

The built-in themes are just category-to-hex maps, so you can reuse one as your palette instead of hand-rolling colours:

val theme = Theme.OneDark
def hex(tok: Token): String =
  hl.categoryOf(tok) match
    case "keyword" => theme.keyword
    case "string"  => theme.string
    case "number"  => theme.number
    case _         => theme.default

Search

Esc
to navigate to open Esc to close