Lumis (Lumis v0.10.0)

Copy Markdown View Source

Syntax highlighter powered by Tree-sitter and Neovim themes.

https://lumis.sh

Features

  • 110+ Tree-sitter languages - Fast, accurate, and updated syntax parsing
  • 250+ built-in Neovim themes - Updated and curated themes from the Neovim community
  • Built-in formatters - HTML (inline/linked), Terminal (ANSI), Multi-theme (light/dark), BBCode
  • Custom formatters - Build your own output
  • Language auto-detection - File extension, shebang, and emacs-mode support
  • Line highlighting - Mark and style individual lines, with custom HTML wrappers
  • Streaming-friendly - Handles incomplete code
  • Parsers are dependencies - Declared in mix.exs, compiled on first use

Installation

Add Lumis and a parser for each language you highlight:

def deps do
  [
    {:lumis, "~> 0.9"},
    {:lumis_wasm_elixir, "~> 0.26.0"}
  ]
end

Usage

iex> Lumis.highlight!("Atom.to_string(:elixir)", formatter: {:html_inline, language: "elixir", theme: "github_light"})

The language is optional — Lumis detects it from the source, a filename, or a shebang. The theme is optional too, but there is no default: without one, :html_inline emits spans with no colors. Themes are named: theme: "github_light", or a Lumis.Theme struct built from your own JSON.

Formatters decide the output: :html_inline, :html_linked, :html_multi_themes, :terminal, :bbcode_scoped, or your own.

For your own, implement Lumis.Formatter and build the output with Lumis.Formatter.HTML or Lumis.Formatter.ANSI, which hold the same pieces the built-in formatters use.

Parsers

A parser is an ordinary dependency: add {:lumis_wasm_elixir, "~> 0.26.0"} and mix deps.get delivers the bytes. Highlighting loads whatever a document needs, including languages injected inside it, and keeps them for every later request. Loading is global to the VM, so only the first process pays.

A language no dependency supplies is not fetched. A document's own language missing is an error — Lumis.ParserError with the package to add — and a language injected inside it missing costs that block its highlighting, not the document. So add the ones a document can inject too, not only the ones it names: Markdown fences reach whatever language they label, HTML reaches css and javascript, and Elixir reaches comment. A bundle package installs a set at once, such as {:lumis_wasm_bundle_web, "~> 0.1"}, and the language catalog at docs.lumis.sh lists every package name.

# move the compile off the first request
Lumis.Languages.load(["elixir", "html", "javascript", "css"])

Application startup

Warm parsers from your application's start/2 so production does not compile them on the first request:

def start(_type, _args) do
  Lumis.Languages.async_load(~w(elixir html javascript css))
  Supervisor.start_link(children(), strategy: :one_for_one, name: MyApp.Supervisor)
end

It returns immediately, so the boot never waits on a compile, and a failed warm-up is logged rather than able to stop the application from starting.

See the deployment guide for the full lifecycle example, bundles, and custom data directories.

The NIF is precompiled. Set LUMIS_BUILD=1 to build it from source instead, or LUMIS_USE_LEGACY_ARTIFACTS=1 to take the legacy-CPU variant on a machine without the newer instruction sets.

It downloads from GitHub Releases, mirrored to Cloudflare R2. Set config :lumis, artifact_source: :cloudflare or LUMIS_ARTIFACT_SOURCE=cloudflare to use the mirror when GitHub is down.

Documentation

Guides for configuration, releases, Phoenix, formatters, themes and recipes are at docs.lumis.sh.

API reference: hexdocs.pm/lumis.

Acknowledgements

  • Makeup for setting up the baseline and for the inspiration
  • Inkjet for the Rust implementation up to v0.2 and for the inspiration

Summary

Types

Highlight lines options for the BBCode formatter.

Highlighter formatter and its options.

Wraps the highlighted code with custom open and close HTML tags.

HTML attributes represented as an ordered keyword list.

Highlight lines options for Inline HTML formatter.

Highlight lines options for Linked HTML formatter.

Options for HTML Multi-Themes formatter.

What an HTML formatter writes around the highlighted tokens.

A language name, filename, or path with extension.

What Lumis knows about one language.

Highlight lines options for the Terminal formatter.

Theme used to apply styles on the highlighted source code.

A built-in theme's name and appearance, without its highlight data.

Functions

Returns every available language and what the catalog knows about it, sorted by id.

Returns every built-in theme's name and appearance, sorted by name.

Returns all default options.

Highlights source code and outputs into a formatted string.

Same as highlight/2 but raises Lumis.HighlightError in case of failure.

Highlights source into the event stream a custom formatter receives, without rendering it.

Returns the ids of the languages loaded into this VM, sorted.

Validates the given options against the options schema.

Types

bbcode_highlight_lines()

@type bbcode_highlight_lines() :: %{lines: [pos_integer() | Range.t()]} | nil

Highlight lines options for the BBCode formatter.

formatter()

@type formatter() ::
  :html_inline
  | {:html_inline,
     language: language(),
     structure: html_structure(),
     theme: theme(),
     pre_class: String.t(),
     pre_attrs: html_attrs(),
     code_attrs: html_attrs(),
     italic: boolean(),
     include_highlights: boolean(),
     highlight_lines: html_inline_highlight_lines(),
     line_numbers: boolean(),
     header: header()}
  | :html_linked
  | {:html_linked,
     language: language(),
     structure: html_structure(),
     pre_class: String.t(),
     pre_attrs: html_attrs(),
     code_attrs: html_attrs(),
     highlight_lines: html_linked_highlight_lines(),
     line_numbers: boolean(),
     header: header()}
  | :html_multi_themes
  | {:html_multi_themes,
     language: language(),
     structure: html_structure(),
     themes: keyword(theme()),
     default_theme: String.t(),
     css_variable_prefix: String.t(),
     pre_class: String.t(),
     pre_attrs: html_attrs(),
     code_attrs: html_attrs(),
     italic: boolean(),
     include_highlights: boolean(),
     highlight_lines: html_inline_highlight_lines(),
     line_numbers: boolean(),
     header: header()}
  | :terminal
  | {:terminal,
     language: language(),
     theme: theme(),
     background: :theme | String.t() | nil,
     width: pos_integer() | nil,
     highlight_lines: terminal_highlight_lines(),
     line_numbers: boolean()}
  | :bbcode_scoped
  | {:bbcode_scoped,
     language: language(), highlight_lines: bbcode_highlight_lines()}
  | module()
  | {module(), keyword()}

Highlighter formatter and its options.

Available formatters: :html_inline, :html_linked, :html_multi_themes, :terminal, :bbcode_scoped

  • :html_inline - generates <span> tags with inline styles for each token, for example: <span style="color: #6eb4bff;">Atom</span>.
  • :html_linked - generates <span> tags with class representing the token type, for example: <span class="l-keyword-special">Atom</span>. Must link an external CSS in order to render colors, see more at HTML Linked.
  • :html_multi_themes - generates HTML with CSS custom properties (variables) for multiple themes, enabling light/dark mode support. Inspired by Shiki Dual Themes.
  • :terminal - generates ANSI escape codes for terminal output.
  • :bbcode_scoped - generates nested BBCode tags using highlight scope names, for example: [keyword-elixir]defmodule[/keyword-elixir].

You can either pass the formatter as an atom to use default options or a tuple with the formatter name and options, so both are equivalent:

# passing only the formatter name like below:
:html_inline
# is the same as passing an empty list of options:
{:html_inline, []}

A custom formatter can be any module implementing Lumis.Formatter, passed either directly or with options:

{MyFormatter, language: "elixir"}

Available Options:

  • html_inline:

    • :language (language/0 - default: nil) - the language used by the formatter. When omitted, Lumis tries to auto-detect it from the source.
    • :structure (html_structure/0 - default: :block) - :block writes a <pre><code> block; :inline writes only the token spans, for markup the page already owns, and ignores the options that configure a block.
    • :theme (theme/0 - default: nil) - the theme to apply styles on the highlighted source code.
    • :pre_class (String.t/0 - default: nil) - the CSS class to append into the wrapping <pre> tag.
    • :pre_attrs (html_attrs/0 - default: []) - attributes merged into the wrapping <pre> tag.
    • :code_attrs (html_attrs/0 - default: []) - attributes merged into the nested <code> tag.
    • :italic (boolean/0 - default: false) - enable italic style for the highlighted code.
    • :include_highlights (boolean/0 - default: false) - include the highlight scope name in a data-highlight attribute. Useful for debugging.
    • :highlight_lines (html_inline_highlight_lines/0 - default: nil) - highlight specific lines either using the theme highlighted style or with custom CSS styling.
    • :line_numbers (boolean/0 - default: false) - open each line with a <span class="l-line-number"> gutter carrying its number.
    • :header (header/0 - default: nil) - wrap the highlighted code with custom open and close HTML tags.
  • html_linked:

    • :language (language/0 - default: nil) - the language used by the formatter. When omitted, Lumis tries to auto-detect it from the source.
    • :structure (html_structure/0 - default: :block) - :block writes a <pre><code> block; :inline writes only the token spans, for markup the page already owns, and ignores the options that configure a block.
    • :pre_class (String.t/0 - default: nil) - the CSS class to append into the wrapping <pre> tag.
    • :pre_attrs (html_attrs/0 - default: []) - attributes merged into the wrapping <pre> tag.
    • :code_attrs (html_attrs/0 - default: []) - attributes merged into the nested <code> tag.
    • :highlight_lines (html_linked_highlight_lines/0 - default: nil) - highlight specific lines either using the l-highlighted class from themes or with a custom CSS class.
    • :line_numbers (boolean/0 - default: false) - open each line with a <span class="l-line-number"> gutter carrying its number.
    • :header (header/0 - default: nil) - wrap the highlighted code with custom open and close HTML tags.
  • html_multi_themes:

    • :language (language/0 - default: nil) - the language used by the formatter. When omitted, Lumis tries to auto-detect it from the source.
    • :structure (html_structure/0 - default: :block) - :block writes a <pre><code> block; :inline writes only the token spans, for markup the page already owns, and ignores the options that configure a block.
    • :themes (keyword(theme()) - required) - keyword list of theme identifiers to theme names/structs. Theme identifiers become CSS class names and CSS variable prefixes. Example: [light: "github_light", dark: "github_dark"].
    • :default_theme (String.t/0 - default: nil) - controls inline color rendering: specify a theme identifier for inline colors, use "light-dark()" for CSS light-dark() function, or nil for CSS variables only.
    • :css_variable_prefix (String.t/0 - default: nil) - CSS variable prefix (defaults to "--lumis" if nil). Generates variables like --lumis-light (color), --lumis-light-bg (background), --lumis-light-font-style, etc.
    • :pre_class (String.t/0 - default: nil) - the CSS class to append into the wrapping <pre> tag.
    • :pre_attrs (html_attrs/0 - default: []) - attributes merged into the wrapping <pre> tag.
    • :code_attrs (html_attrs/0 - default: []) - attributes merged into the nested <code> tag.
    • :italic (boolean/0 - default: false) - enable italic style for the highlighted code.
    • :include_highlights (boolean/0 - default: false) - include the highlight scope name in a data-highlight attribute.
    • :highlight_lines (html_inline_highlight_lines/0 - default: nil) - highlight specific lines (same as html_inline).
    • :line_numbers (boolean/0 - default: false) - open each line with a <span class="l-line-number"> gutter carrying its number.
    • :header (header/0 - default: nil) - wrap the highlighted code with custom open and close HTML tags.
  • terminal:

    • :language (language/0 - default: nil) - the language used by the formatter. When omitted, Lumis tries to auto-detect it from the source.
    • :theme (theme/0 - default: nil) - the theme to apply styles on the highlighted source code.
    • :background (:theme | t:String.t/0 | nil - default: nil) - fallback background behavior: nil inherits the output background, :theme uses the theme's normal background color, and a string uses that color.

    • :width (pos_integer() | nil - default: nil) - pad each rendered terminal line to the given width. This is most useful with :background.

    • :highlight_lines (terminal_highlight_lines/0 - default: nil) - paint specific lines with a background colour, either :background or the theme's highlighted background.
    • :line_numbers (boolean/0 - default: false) - prefix each line with its number, right-aligned to the widest one and dimmed with the theme's comment colour.
  • bbcode_scoped:

    • :language (language/0 - default: nil) - available when passed as {:bbcode_scoped, ...}.
    • :highlight_lines (bbcode_highlight_lines/0 - default: nil) - wrap specific lines in [highlighted]...[/highlighted].

Examples

Inline HTML formatter with default options

:html_inline

There is no default theme, so this emits <span> tags without any style attribute. Pass :theme to get colors, or use :html_linked to style the output with a CSS file.

Inline HTML formatter with custom options

{:html_inline, theme: "onedark", pre_class: "example-01", include_highlights: true}

HTML Inline: highlight specific lines

# apply theme's `highlighted` style
{:html_inline, theme: "onedark", highlight_lines: %{lines: [2..4, 6], style: :theme}}

# style: :theme is the default
{:html_inline, theme: "onedark", highlight_lines: %{lines: [1, 2, 3]}}

# explicitly use theme style
{:html_inline, theme: "onedark", highlight_lines: %{lines: [1, 2, 3], style: :theme}}

# overrides default style
{:html_inline, theme: "onedark", highlight_lines: %{lines: [1, 3..5, 8], style: "background-color: #fff3cd; border-left: 3px solid #ffc107;"}}

# with only class and no style
{:html_inline, theme: "onedark", highlight_lines: %{lines: [1, 2, 3], style: nil, class: "transition-colors duration-500 w-full inline-block bg-yellow-500"}}

HTML Linked: highlight specific lines

# use default `l-highlighted` class (already present in themes)
{:html_linked, highlight_lines: %{lines: [2..4, 6]}}

# use custom class
{:html_linked, highlight_lines: %{lines: [1, 2, 3], class: "error-line"}}

Inline structure: highlight code inside a sentence

# only the token spans, to put inside a `<code>` the page already has
{:html_inline, theme: "onedark", structure: :inline}

Wrap with custom open and close HTML tags

header = %{
  open_tag: "<div class="code-header"><span>file: app.ex</span>",
  close_tag: "</div>"
}
{:html_inline, theme: "onedark", header: header}

HTML Multi-Themes: Light/Dark mode support

# Basic dual theme with CSS variables
{:html_multi_themes, themes: [light: "github_light", dark: "github_dark"]}

# With light-dark() function for automatic theme switching based on system preference
{:html_multi_themes,
 themes: [light: "github_light", dark: "github_dark"],
 default_theme: "light-dark()"}

# With inline colors for default theme and CSS variables for others
{:html_multi_themes,
 themes: [light: "github_light", dark: "github_dark"],
 default_theme: "light"}

# Multiple themes with custom prefix
{:html_multi_themes,
 themes: [light: "github_light", dark: "github_dark", dim: "catppuccin_frappe"],
 css_variable_prefix: "--code"}

# With Theme structs instead of strings
light_theme = Lumis.Theme.get("github_light")
dark_theme = Lumis.Theme.get("github_dark")
{:html_multi_themes, themes: [light: light_theme, dark: dark_theme]}

Terminal formatter

:terminal

{:terminal, theme: "github_light"}

{:terminal, theme: "dracula", background: :theme, width: 120}

{:terminal, theme: "dracula", background: "#282a36", width: 120}

BBCode Scoped formatter

:bbcode_scoped

Emits highlight scope names as tags, not standard forum-style BBCode like [b], [color], or [code].

See https://docs.rs/lumis/latest/lumis/enum.FormatterOption.html for more info.

header()

@type header() :: %{close_tag: String.t(), open_tag: String.t()} | nil

Wraps the highlighted code with custom open and close HTML tags.

highlight_events_options()

@type highlight_events_options() :: [
  annotations: [keyword()],
  rainbow_brackets: boolean(),
  budget: keyword()
]

Options for highlight_events/3.

  • :annotations (keyword/0) - Caller-provided semantic ranges for this highlighting operation. Each is a keyword list with either an :offset or a :position range and optional :data:

    annotations: [
      [offset: {12, 23}, data: %{change: :added}],
      [position: {{1, 10}, {1, 21}}, data: %{change: :removed}]
    ]

    A formatter receives each opening event as {:annotation_start, %{range: {start, end}, data: data}}, with the range resolved to byte offsets. An empty range is a point.

    The default value is [].

  • :rainbow_brackets (boolean/0) - Render nested brackets with rainbow bracket decorations. The default value is false.

  • :budget (keyword/0) - The work one render is allowed to do:

    budget: [time_limit: 1_000, match_limit: 16_384]

    Both dimensions bound the same render, so they are one option. nil selects the default for either key.

    The default value is [].

    • :time_limit - How long one render may take, in milliseconds, 0 for no bound, or nil for the default of 5000.

      A render that runs out returns the whole file as plain text rather than an error, and HTML formatters mark it data-lumis-budget="time". Loading a language is not counted against it.

      The default value is nil.

    • :match_limit - Bound on the query matches Tree-sitter keeps in progress at once, for the highlight and bracket queries alike, or nil for the default.

      Tree-sitter walks its whole pool of in-progress matches before it emits each capture, so the bound is what keeps highlighting linear on documents whose markup nests deeply enough to keep many matches open at once. Raising it recovers matches that would otherwise be dropped on such documents, at that cost.

      The default value is nil.

html_attrs()

@type html_attrs() :: keyword(String.t() | boolean())

HTML attributes represented as an ordered keyword list.

A string value writes name="value". true writes the bare name HTML gives boolean attributes such as hidden, and false removes an attribute Lumis would otherwise generate.

html_inline_highlight_lines()

@type html_inline_highlight_lines() ::
  %{
    lines: [pos_integer() | Range.t()],
    style: :theme | String.t() | nil,
    class: String.t() | nil
  }
  | nil

Highlight lines options for Inline HTML formatter.

html_linked_highlight_lines()

@type html_linked_highlight_lines() ::
  %{lines: [pos_integer() | Range.t()], class: String.t()} | nil

Highlight lines options for Linked HTML formatter.

html_multi_themes_options()

@type html_multi_themes_options() ::
  %{
    structure: html_structure(),
    themes: keyword(theme()),
    default_theme: String.t() | nil,
    css_variable_prefix: String.t() | nil,
    pre_class: String.t() | nil,
    pre_attrs: html_attrs(),
    code_attrs: html_attrs(),
    italic: boolean(),
    include_highlights: boolean(),
    highlight_lines: html_inline_highlight_lines() | nil,
    line_numbers: boolean(),
    header: header()
  }
  | nil

Options for HTML Multi-Themes formatter.

The themes are specified as a keyword list where keys are CSS identifiers (atoms) and values are theme names (strings) or Theme structs.

html_structure()

@type html_structure() :: :block | :inline

What an HTML formatter writes around the highlighted tokens.

:block is a <pre><code> block holding one <span class="l-line"> per line. :inline is the token spans and the text between them, and nothing else, for a <code> or other element the page already owns. Lines are separated by \n and the last one has no terminator, as in a block. :pre_class, :pre_attrs, :code_attrs, :highlight_lines, :line_numbers and :header have no effect. The theme's text and background color are not written either, because a block writes them on its <pre>; put Lumis.Formatter.HTML.pre_attrs/1 on your own element to keep them.

language()

@type language() :: String.t() | nil

A language name, filename, or path with extension.

See Lumis.available_languages/0 to list all available languages or check out a list of available languages.

Examples

- "elixir"
- ".ex"
- "app.ex"
- "lib/app.ex"

language_info()

@type language_info() :: %{
  id: String.t(),
  name: String.t(),
  aliases: [String.t()],
  extensions: [String.t()],
  globs: [String.t()],
  emacs_modes: [String.t()],
  shebangs: [String.t()]
}

What Lumis knows about one language.

options()

@type options() :: [
  language: language(),
  formatter: formatter(),
  theme: struct() | binary() | nil,
  inline_style: boolean(),
  pre_class: binary() | nil,
  annotations: [keyword()],
  rainbow_brackets: boolean(),
  budget: keyword()
]
  • :language (Lumis.language/0) - This option is deprecated. Use the :language option inside the formatter tuple instead, eg: {:html_inline, language: "elixir"}

  • :formatter (Lumis.formatter/0) - Formatter to apply on the highlighted source code. See the type doc for more info. The default value is {:html_inline, []}.

  • :theme - This option is deprecated. Use :formatter instead.

  • :inline_style (boolean/0) - This option is deprecated. Use :formatter instead.

  • :pre_class - This option is deprecated. Use :formatter instead.

  • :annotations (keyword/0) - Caller-provided semantic ranges for this highlighting operation. Each is a keyword list with either an :offset or a :position range and optional :data:

    annotations: [
      [offset: {12, 23}, data: %{change: :added}],
      [position: {{1, 10}, {1, 21}}, data: %{change: :removed}]
    ]

    A formatter receives each opening event as {:annotation_start, %{range: {start, end}, data: data}}, with the range resolved to byte offsets. An empty range is a point.

    The default value is [].

  • :rainbow_brackets (boolean/0) - Render nested brackets with rainbow bracket decorations. The default value is false.

  • :budget (keyword/0) - The work one render is allowed to do:

    budget: [time_limit: 1_000, match_limit: 16_384]

    Both dimensions bound the same render, so they are one option. nil selects the default for either key.

    The default value is [].

    • :time_limit - How long one render may take, in milliseconds, 0 for no bound, or nil for the default of 5000.

      A render that runs out returns the whole file as plain text rather than an error, and HTML formatters mark it data-lumis-budget="time". Loading a language is not counted against it.

      The default value is nil.

    • :match_limit - Bound on the query matches Tree-sitter keeps in progress at once, for the highlight and bracket queries alike, or nil for the default.

      Tree-sitter walks its whole pool of in-progress matches before it emits each capture, so the bound is what keeps highlighting linear on documents whose markup nests deeply enough to keep many matches open at once. Raising it recovers matches that would otherwise be dropped on such documents, at that cost.

      The default value is nil.

See each option type for more info.

terminal_highlight_lines()

@type terminal_highlight_lines() ::
  %{lines: [pos_integer() | Range.t()], background: String.t() | nil} | nil

Highlight lines options for the Terminal formatter.

theme()

@type theme() :: String.t() | Lumis.Theme.t() | nil

Theme used to apply styles on the highlighted source code.

See Lumis.available_themes/0 to list all available themes or check out a list of available themes.

theme_info()

@type theme_info() :: %{name: String.t(), appearance: String.t()}

A built-in theme's name and appearance, without its highlight data.

Functions

available_languages()

@spec available_languages() :: [language_info()]

Returns every available language and what the catalog knows about it, sorted by id.

Example

iex> Lumis.available_languages() |> Enum.find(&(&1.id == "elixir"))
%{
  id: "elixir",
  name: "Elixir",
  aliases: [],
  extensions: ["*.ex", "*.exs"],
  globs: ["*.ex", "*.exs"],
  emacs_modes: ["elixir"],
  shebangs: ["elixir"]
}

available_themes()

@spec available_themes() :: [theme_info()]

Returns every built-in theme's name and appearance, sorted by name.

Use Lumis.Theme.get/2 to get the actual theme struct.

Example

iex> Lumis.available_themes() |> Enum.find(&(&1.name == "github_light"))
%{name: "github_light", appearance: "light"}

default_options()

@spec default_options() :: options()

Returns all default options.

highlight(source, options \\ [])

@spec highlight(String.t(), options()) ::
  {:ok, String.t()} | {:error, Lumis.RenderError.t()}

Highlights source code and outputs into a formatted string.

A language whose parser is not installed as a dependency renders as plain text.

Invalid options still raise, because those are a caller mistake rather than a runtime condition.

Options

See options/0.

Examples

Defining the language name:

iex> Lumis.highlight("Atom.to_string(:elixir)", formatter: {:html_inline, language: "elixir"})
{
  :ok,
  <pre class="lumis" style="color: #abb2bf; background-color: #282c34;"><code class="language-elixir" translate="no" tabindex="0"><span class="l-line" data-line="1"><span style="color: #e5c07b;">Atom</span><span style="color: #56b6c2;">.</span><span style="color: #61afef;">to_string</span><span style="color: #c678dd;">(</span><span style="color: #e06c75;">:elixir</span><span style="color: #c678dd;">)</span></span></code></pre>
}

Guessing the language based on the provided source code:

iex> Lumis.highlight("#!/usr/bin/env bash\nID=1")
{:ok, "<pre class="lumis" ...><code class="language-bash" ...>...</code></pre>"}

With custom options:

iex> Lumis.highlight("Atom.to_string(:elixir)", formatter: {:html_inline, language: "example.ex", pre_class: "example-elixir"})
{:ok, "<pre class="lumis example-elixir" ...><code ...>...</code></pre>"}

Terminal formatter:

iex> Lumis.highlight("Atom.to_string(:elixir)", formatter: {:terminal, language: "elixir"})
{:ok, "Atom.to_string(:elixir)"}

Highlighting specific lines in HTML Inline formatter:

iex> code = """
...> defmodule Example do
...>   @lang = :elixir
...>   def lang, do: @lang
...> end
...> """
iex> highlight_lines = %{lines: [2]}
iex> Lumis.highlight(code, formatter: {:html_inline, language: "elixir", highlight_lines: highlight_lines})
# Line 2 will be highlighted with the theme's `highlighted` style:
<span class="l-line" style="display: inline-block; width: 100%; min-height: 1lh; vertical-align: top; background-color: #414858;" data-line="2">...</span>

Highlighting specific lines in HTML Linked formatter:

iex> code = """
...> defmodule Example do
...>   @lang = :elixir
...>   def lang, do: @lang
...> end
...> """
iex> highlight_lines = %{lines: [2]}
iex> Lumis.highlight(code, formatter: {:html_linked, language: "elixir", highlight_lines: highlight_lines})
# Line 2 will contain a `l-highlighted` class:
<span class="l-line l-highlighted" data-line="2">...

Wrapping with custom HTML:

iex> header = %{
...>   open_tag: "<figure><span>file: example.exs</span>",
...>   close_tag: "</figure>"
...> }
iex> Lumis.highlight("IO.puts('hello')", formatter: {:html_inline, language: "elixir", header: header})
{:ok, "<figure><span>file: example.exs</span><pre...><code ...>...</code></pre></figure>"}

See https://docs.rs/lumis/latest/lumis/fn.highlight.html for more info.

highlight(language, source, options)

This function is deprecated. Use highlight/2 instead.

highlight!(source, options \\ [])

@spec highlight!(String.t(), keyword()) :: String.t()

Same as highlight/2 but raises Lumis.HighlightError in case of failure.

highlight!(language, source, options)

This function is deprecated. Use highlight!/2 instead.

highlight_events(source, language, options \\ [])

@spec highlight_events(String.t(), String.t() | nil, highlight_events_options()) ::
  {:ok, [Lumis.Formatter.event(term())]} | {:error, Lumis.RenderError.t()}

Highlights source into the event stream a custom formatter receives, without rendering it.

Reach for it when a formatter's single string is the wrong shape for the result, such as one HTML fragment per line:

{:ok, events} = Lumis.highlight_events(source, "elixir")
Lumis.Formatter.HTML.render_lines_from_events(source, events, attrs)

language is a language name, a file name or path, or nil to detect it from source. A language whose parser is not installed comes back as one plain :source event.

The counterpart of highlight_events in Rust and highlightEvents() in JavaScript.

Options

See highlight_events_options/0.

Example

iex> {:ok, events} = Lumis.highlight_events("x = 1", "elixir")
iex> Enum.take(events, 3)
[{:start, %{scope: "variable", language: "elixir"}}, {:source, %{start: 0, end: 1}}, :end]

highlight_events!(source, language, options \\ [])

@spec highlight_events!(String.t(), String.t() | nil, highlight_events_options()) :: [
  Lumis.Formatter.event(term())
]

Same as highlight_events/3 but raises Lumis.HighlightError in case of failure.

loaded_languages()

@spec loaded_languages() :: [id :: String.t()]

Returns the ids of the languages loaded into this VM, sorted.

The complement of available_languages/0: what can be highlighted right now without a download. Loading is global to the VM, so this is the same list in every process.

Example

iex> Lumis.Languages.load("elixir")
iex> Lumis.loaded_languages()
["elixir"]

validate_options!(options)

@spec validate_options!(options()) :: options()

Validates the given options against the options schema.

This function validates the provided options using NimbleOptions and the defined schema. It ensures that all options are valid and properly typed before being passed to the highlighting functions.

Examples

iex> Lumis.validate_options!(formatter: {:html_inline, language: "elixir"})
[formatter: {:html_inline, [header: nil, line_numbers: nil, highlight_lines: nil, include_highlights: false, italic: false, pre_class: nil, theme: nil, language: "elixir"]}]

iex> Lumis.validate_options!(formatter: {:html_inline, theme: "dracula"})
[formatter: {:html_inline, [theme: "dracula", ...]}]

iex> Lumis.validate_options!(language: :invalid)
** (NimbleOptions.ValidationError)