Lumis

Syntax highlighter powered by Tree-sitter and Neovim themes.

https://lumis.sh

Hex VersionHex DocsMIT

Features

Installation

def deps do
  [
    {:lumis, "~> 0.1"}
  ]
end

Usage

Basic Usage (HTML Inline)

iex> Lumis.highlight!("Atom.to_string(:elixir)", language: "elixir")
~s|<pre class="lumis" style="color: #abb2bf; background-color: #282c34;"><code class="language-elixir" translate="no" tabindex="0"><div class="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>|

See the HTML Linked and Terminal formatters below for more options.

Language Auto-detection

iex> Lumis.highlight!("#!/usr/bin/env bash\nID=1")
~s|<pre class="lumis" style="color: #abb2bf; background-color: #282c34;"><code class="language-bash" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #c678dd;">#!/usr/bin/env bash</span>
</div><div class="line" data-line="2"><span style="color: #d19a66;">ID</span><span style="color: #56b6c2;">=</span><span style="color: #d19a66;">1</span>
</span></code></pre>|

Themes

Themes are sourced from popular Neovim colorschemes.

Use Lumis.available_themes/0 to list all available themes. You can specify a theme by name in the formatter options, or use Lumis.Theme.get/1 to get a specific theme struct if you need to inspect or manipulate its styles.

# Using theme name in formatter options
iex> Lumis.highlight!("setTimeout(fun, 5000);", language: "js", formatter: {:html_inline, theme: "github_light"})
~s|<pre class="lumis" style="color: #1f2328; background-color: #ffffff;"><code class="language-javascript" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #6639ba;">setTimeout</span><span style="color: #1f2328;">(</span><span style="color: #1f2328;">fun</span><span style="color: #1f2328;">,</span> <span style="color: #0550ae;">5000</span><span style="color: #1f2328;">)</span><span style="color: #1f2328;">;</span>
</span></code></pre>|

# Using theme struct
iex> theme = Lumis.Theme.get("github_light")
iex> Lumis.highlight!("setTimeout(fun, 5000);", language: "js", formatter: {:html_inline, theme: theme})

Bring Your Own Theme

You can also load custom themes from JSON files or strings:

# Load from JSON file
{:ok, theme} = Lumis.Theme.from_file("/path/to/your/theme.json")
Lumis.highlight!("your code", theme: theme)

# Load from JSON string
theme_json = ~s({"name": "my_theme", "appearance": "dark", "highlights": {"comment": {"fg": "#808080"}}})
{:ok, theme} = Lumis.Theme.from_json(theme_json)
Lumis.highlight!("your code", theme: theme)

Incomplete or Malformed code

It’s also capable of handling incomplete or malformed code, useful for streaming like in a ChatGPT interface:

iex> Lumis.highlight!("const header = document.getEl", language: "js")
~s|<pre class="lumis" style="color: #abb2bf; background-color: #282c34;"><code class="language-javascript" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #c678dd;">const</span> <span style="color: #abb2bf;">header</span> <span style="color: #abb2bf;">=</span> <span style="color: #e86671;">document</span><span style="color: #848b98;">.</span><span style="color: #56b6c2;">getEl</span>
</span></code></pre>|

Formatters

Lumis supports four output formatters:

All HTML formatters wrap each line in a <div class="line"> element with a data-line attribute containing the line number, making it easy to add line numbers or implement line-based features in your application.

See the Livebook examples and t:formatter/0 for more.

HTML Inline (Default)

Generates HTML with inline styles for each token:

iex> Lumis.highlight!("Atom.to_string(:elixir)", language: "elixir", formatter: :html_inline)
# or with options
iex> Lumis.highlight!("Atom.to_string(:elixir)", language: "elixir", formatter: {:html_inline, pre_class: "my-code", italic: true, include_highlights: true})

Options:

HTML Linked

Generates HTML with CSS classes for styling:

iex> Lumis.highlight!("Atom.to_string(:elixir)", language: "elixir", formatter: :html_linked)
# or with options
iex> Lumis.highlight!("Atom.to_string(:elixir)", language: "elixir", formatter: {:html_linked, pre_class: "my-code"})

Options:

To use linked styles, you need to include one of the available CSS themes in your app.

For Phoenix apps, add this to your endpoint.ex:

plug Plug.Static,
  at: "/themes",
  from: {:lumis, "priv/static/css/"},
  only: ["dracula.css"] # choose any theme you want

Then add the stylesheet to your template:

<link phx-track-static rel="stylesheet" href={~p"/themes/dracula.css"} />

HTML Multi-Themes

Generates HTML with CSS custom properties (variables) for multiple themes, enabling light/dark mode support. Inspired by Shiki Dual Themes.

# Basic dual theme with CSS variables
iex> Lumis.highlight!("Atom.to_string(:elixir)",
  language: "elixir",
  formatter: {:html_multi_themes,
    themes: [light: "github_light", dark: "github_dark"]
  }
)

# With light-dark() function for automatic theme switching
iex> Lumis.highlight!("Atom.to_string(:elixir)",
  language: "elixir",
  formatter: {:html_multi_themes,
    themes: [light: "github_light", dark: "github_dark"],
    default_theme: "light-dark()"
  }
)

The generated HTML includes CSS custom properties like --lumis-light, --lumis-dark, --lumis-{theme}-bg, and font styling variables (-font-style, -font-weight, -text-decoration) that can be used with CSS media queries or JavaScript for theme switching:

/* Automatic light/dark mode based on system preference */
@media (prefers-color-scheme: dark) {
  .lumis,
  .lumis span {
    color: var(--lumis-dark) !important;
    background-color: var(--lumis-dark-bg) !important;
    font-style: var(--lumis-dark-font-style) !important;
    font-weight: var(--lumis-dark-font-weight) !important;
    text-decoration: var(--lumis-dark-text-decoration) !important;
  }
}

/* Manual control with class-based switching */
html.dark .lumis,
html.dark .lumis span {
  color: var(--lumis-dark) !important;
  background-color: var(--lumis-dark-bg) !important;
  font-style: var(--lumis-dark-font-style) !important;
  font-weight: var(--lumis-dark-font-weight) !important;
  text-decoration: var(--lumis-dark-text-decoration) !important;
}

Options:

Terminal

Generates ANSI escape codes for terminal output:

iex> Lumis.highlight!("Atom.to_string(:elixir)", language: "elixir", formatter: :terminal)
# or with options
iex> Lumis.highlight!("Atom.to_string(:elixir)", language: "elixir", formatter: {:terminal, theme: "github_light"})

Options:

Samples

Visit https://lumis.sh to check out some examples.

Acknowledgements