# Lumis

<!-- MDOC -->

<p align="center">
  Syntax highlighter powered by Tree-sitter and Neovim themes.
</p>

<p align="center">
  <a href="https://lumis.sh">https://lumis.sh</a>
</p>

<div align="center">
  <a href="https://hex.pm/packages/lumis">
    <img alt="Hex Version" src="https://img.shields.io/hexpm/v/lumis">
  </a>

  <a href="https://hexdocs.pm/lumis">
    <img alt="Hex Docs" src="http://img.shields.io/badge/hex.pm-docs-green.svg?style=flat">
  </a>

  <a href="https://opensource.org/licenses/MIT">
    <img alt="MIT" src="https://img.shields.io/hexpm/l/lumis">
  </a>
</div>

## 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
- **Load parsers on demand** - Verified and cached, including injected languages

## Installation

```elixir
def deps do
  [
    {:lumis, "~> 0.3"}
  ]
end
```

## Usage

```elixir
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.

## Parsers

Highlighting downloads, verifies and loads whatever a document needs, including
languages injected inside it, and caches them for every later request. Loading is
global to the VM, so only the first process pays.

```elixir
# move the download 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 download
or compile them on the first request:

```elixir
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 the network, and a failed
warm-up is logged rather than able to stop the application from starting.

See the [deployment guide](https://lumis.hexdocs.pm/deployment.html) for the
full lifecycle example, bundles, the standalone CLI, and custom cache 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, see
[where the precompiled NIF comes from](https://lumis.sh/docs/usage/elixir-integration#where-the-precompiled-nif-comes-from).

## Documentation

- [Elixir integration](https://lumis.sh/docs/usage/elixir-integration) — configuration, releases, Phoenix
- [Formatters](https://lumis.sh/docs/usage/formatters) — every formatter and its options
- [Themes](https://lumis.sh/docs/usage/themes) — the theme list, custom themes, CSS files
- [Languages](https://lumis.sh/docs/reference/languages) — what is supported and how detection works
- [Line highlighting](https://lumis.sh/docs/usage/line-highlighting)
- [Recipes](https://lumis.sh/docs/recipes) — LiveView rendering, light/dark, injected languages

API reference: [hexdocs.pm/lumis](https://hexdocs.pm/lumis).

## Acknowledgements

* [Makeup](https://hex.pm/packages/makeup) for setting up the baseline and for the inspiration
* [Inkjet](https://crates.io/crates/inkjet) for the Rust implementation up to v0.2 and for the inspiration
