glimra

Package Version Hex Docs Erlang-compatible

Zero runtime syntax highlighter for lustre/ssg.

In Swedish, glimra describes the brilliant gleam or lustre that comes from a polished, reflective surface, capturing the essence of light shining off something smooth and glossy.

Platform support

glimra uses NIFs to extract syntax highlighting events provided by the tree-sitter and tree-sitter-highlight crates. This allows glimra to provide syntax highlighting for a wide range of languages with minimal effort.

Unfortunately, this also means that glimra only works with the Erlang target. You can use the provided precompiled binaries for the supported platforms or compile the NIFs yourself with the Rust toolchain.

Currently, the following operating systems and architectures are supported:

OSArchitecture(s)
Linuxx86_64
macOSaarch64, x86_64
Windowsx86_64

Installation

gleam add glimra

Usage

glimra can be used to highlight source code snippets in a variety of languages. While the primary use case is to provide syntax highlighting for code snippets in the lustre/ssg static site generator, it can also be used in a more standalone fashion:

import glimra
import glimra/theme

pub fn main() {
  let syntax_highlighter =
    glimra.new_syntax_highlighter() |> glimra.set_theme(theme.default_theme())

  let source = "let greeting = \"Hello, Joe!\""
  let language = "gleam"

  let highlighted_snippet =
    syntax_highlighter
    |> glimra.syntax_highlight(source:, language:)

  let css = syntax_highlighter |> glimra.to_css()
}

Usage with lustre/ssg

glimra provides a set of utilities to make it easier to work with glimra and lustre/ssg.

Given a typical lustre/ssg build.gleam file with the following main() function, you can generate and add the static stylesheet using the add_static_stylesheet builder:

import glimra
import glimra/theme

pub fn main() {
  let syntax_highlighter =
    glimra.new_syntax_highlighter()
    |> glimra.set_theme(theme.default_theme())

  let build = ssg.new("./priv")
    |> ssg.add_static_route("/", index.view())
    |> ssg.add_static_route("/blog", blog.view(posts.all()))
    // the `add_static_stylesheet` builder works with your existing `lustre/ssg` config
    |> glimra.add_static_stylesheet(syntax_highlighter: syntax_highlighter)
    |> ssg.build
}

You can also link to the generated static stylesheet in your header:

import glimra

fn head(title: String, description: String) {
  html.head([], [
    html.title([], title),
    html.meta([attribute.attribute("charset", "utf-8")]),
    html.meta([
      attribute.attribute("name", "viewport"),
      attribute.attribute("content", "width=device-width, initial-scale=1"),
    ]),
    html.link([attribute.href("/style.css"), attribute.rel("stylesheet")]),
    // `link_static_stylesheet` will use the same path as `add_static_stylesheet`
    glimra.link_static_stylesheet(),
  ])
}

Finally, you can use the included codeblock_renderer with your lustre/ssg/djot render configuration:

pub fn parse(
  from filepath: String,
  // pass in the same `syntax_highlighter` you used to generate the stylesheet
  syntax_highlighter syntax_highlighter: Config(HasTheme),
) -> String {
  let renderer =
    djot.Renderer(
      ..djot.default_renderer(),
      // this will use `codeblock_renderer` to highlight code snippets
      codeblock: glimra.codeblock_renderer(syntax_highlighter),
    )

  let content = {
    use file <- result.try(
      simplifile.read(filepath) |> result.replace_error(Nil),
    )
    djot.render(file, renderer)
  }

  case content {
    Ok(content) -> content
    Error(_) -> {
      let error_message = "could not parse content from file: " <> filepath
      panic as error_message
    }
  }
}

Configuration

glimra can be configured to include line numbers, to keep or trim whitespace, and to use a custom theme. The following configuration options are available:

pub type Config(HasTheme) {
  line_numbers: Bool,
  trim_whitespace: Bool,
  theme: theme.Theme,
}

These are set with the builder pattern:

let syntax_highlighter =
  glimra.new_syntax_highlighter()
  |> glimra.set_line_numbers(true)
  |> glimra.set_trim_whitespace(true)
  |> glimra.set_theme(theme.default_theme())

Note that the theme is required for any CSS generation to work!


Further documentation can be found at https://hexdocs.pm/glimra.

Development

gleam test  # run the tests
Search Document