defmodule Autumn do @external_resource "README.md" @moduledoc "README.md" |> File.read!() |> String.split("") |> Enum.fetch!(1) @typedoc """ A language name, filename, or extesion. ## Examples - "elixir" - "main.rb" - ".rs" - "php" """ @type lang_filename_ext :: String.t() | nil @deprecated "Use highlight/2 instead" def highlight(lang_filename_ext, source_code, opts) do IO.warn(""" passing the language in the first parameter is deprecated, pass a `:language` option instead: Autumn.highlight("import Kernel", language: "elixir") """) opts = Keyword.put(opts, :language, lang_filename_ext) highlight(source_code, opts) end @deprecated "Use highlight!/2 instead" def highlight!(lang_filename_ext, source_code, opts) do IO.warn(""" passing the language in the first parameter is deprecated, pass a `:language` option instead: Autumn.highlight!("import Kernel", language: "elixir") """) opts = Keyword.put(opts, :language, lang_filename_ext) highlight!(source_code, opts) end @doc """ Highlights the `source_code`. ## Options * `:language` (default: `nil`) - the language to use for highlighting. Besides a language name, you can also pass a filename or extension. Note that an invalid language or `nil` will fallback to rendering plain text (no colors) using the background and foreground colors defined by the current theme. * `:theme` (default: `"onedark"`) - accepts any theme listed [here](https://github.com/leandrocp/autumn/tree/main/priv/themes). You should pass the filename without special chars and without extension, for example pass `theme: "adwaita_dark"` to use the [Adwaita Dark](https://github.com/leandrocp/autumn/blob/main/priv/themes/adwaita-dark.toml) theme or `theme: "penumbra"` to use the [Penumbra+](https://github.com/leandrocp/autumn/blob/main/priv/themes/penumbra%2B.toml) theme. * `:pre_class` (default: `nil`) - the CSS class to inject into the wrapping parent `
` tag.
By default it renders a `` tag with the `autumn-hl` class which will be merged with the class passed into this option.
* `:inline_style` (default: `true`) - see more info on the module doc.
## Examples
Passing a language name:
iex> {:ok, hl} = Autumn.highlight("Atom.to_string(:elixir)", language: "elixir")
iex> IO.puts(hl)
#=> Atom.to_string(:elixir)
Or more options with a file extension:
iex> {:ok, hl} = Autumn.highlight("Atom.to_string(:elixir)", language: ".ex", inline_style: false, pre_class: "my-app-code")
iex> IO.puts(hl)
#=> Atom.to_string(:elixir)
And no language results in plain text:
iex> {:ok, hl} = Autumn.highlight("Atom.to_string(:elixir)")
iex> IO.puts(hl)
#=> Atom.to_string(:elixir)
"""
@spec highlight(String.t(), keyword()) :: {:ok, String.t()} | {:error, term()}
def highlight(source_code, opts \\ [])
def highlight(source_code, opts) when is_binary(source_code) and is_list(opts) do
language = opts |> Keyword.get(:language, nil) |> language()
theme = Keyword.get(opts, :theme, "onedark")
pre_class = Keyword.get(opts, :pre_class, nil)
inline_style = Keyword.get(opts, :inline_style, true)
Autumn.Native.highlight(language, source_code, theme, pre_class, inline_style)
end
def highlight(lang_filename_ext, source_code)
when is_binary(lang_filename_ext) and is_binary(source_code) do
highlight(source_code, language: lang_filename_ext)
end
@doc """
Same as `highlight/2` but raises in case of failure.
"""
@spec highlight!(String.t(), keyword()) :: String.t()
def highlight!(source_code, opts \\ [])
def highlight!(source_code, opts) when is_binary(source_code) and is_list(opts) do
case highlight(source_code, opts) do
{:ok, highlighted} ->
highlighted
{:error, error} ->
raise """
failed to highlight source code
Got:
#{inspect(error)}
"""
end
end
def highlight!(lang_filename_ext, source_code)
when is_binary(lang_filename_ext) and is_binary(source_code) do
highlight!(source_code, language: lang_filename_ext)
end
@doc false
def language(lang_filename_ext) when is_binary(lang_filename_ext) do
lang_filename_ext
|> String.downcase()
|> Path.basename()
|> do_language
end
def language(_lang_filename_ext), do: "plaintext"
defp do_language(<<"."::binary, ext::binary>>), do: ext
defp do_language(lang) when is_binary(lang) do
case lang |> Path.basename() |> Path.extname() do
"" -> lang
ext -> do_language(ext)
end
end
end