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