diff --git a/lib/makeup/lexers/elixir_lexer.ex b/lib/makeup/lexers/elixir_lexer.ex index 48e17b6..14566ef 100644 --- a/lib/makeup/lexers/elixir_lexer.ex +++ b/lib/makeup/lexers/elixir_lexer.ex @@ -502,6 +502,35 @@ defmodule Makeup.Lexers.ElixirLexer do defp postprocess_helper([{:name, attrs, "_" <> _name = text} | tokens]), do: [{:comment, attrs, text} | postprocess_helper(tokens)] + # Custom sigil lexers + defp postprocess_helper([{:string_sigil, attrs, content} | tokens]) do + # content is a list of the format ["~", sigil_char, separator, ... sigil_content ..., end_separator] + sigil = + content + |> Enum.at(1) + |> List.wrap() + |> List.to_string() + + lexer = Map.get(get_sigil_lexers(), sigil) + + if lexer do + ["~", _sigil, separator | content_with_end_separator] = content + end_separator = Enum.at(content_with_end_separator, -1) + content = Enum.slice(content_with_end_separator, 0..-2//1) |> List.to_string() + + inner_tokens = lexer.lex(content) + + List.flatten([ + {:string_sigil, attrs, "~#{sigil}#{separator}"}, + inner_tokens, + [{:string_sigil, attrs, end_separator}] + | postprocess_helper(tokens) + ]) + else + [{:string_sigil, attrs, content} | postprocess_helper(tokens)] + end + end + # Otherwise, don't do anything with the current token and go to the next token. defp postprocess_helper([token | tokens]), do: [token | postprocess_helper(tokens)] @@ -621,4 +650,21 @@ defmodule Makeup.Lexers.ElixirLexer do |> postprocess([]) |> match_groups(group_prefix) end + + @doc """ + Register a custom lexer to be used for lexing sigil contents. + + ## Examples + + > Makeup.Lexers.ElixirLexer.register_sigil_lexer("H", Makeup.Lexers.HEExLexer) + + """ + def register_sigil_lexer(sigil, lexer) do + lexers = get_sigil_lexers() + Application.put_env(:makeup_elixir, :sigil_lexers, Map.put(lexers, sigil, lexer)) + end + + defp get_sigil_lexers() do + Application.get_env(:makeup_elixir, :sigil_lexers, %{}) + end end diff --git a/test/makeup/lexers/elixir_lexer/elixir_lexer_tokenizer_test.exs b/test/makeup/lexers/elixir_lexer/elixir_lexer_tokenizer_test.exs index f93d185..dcec0b9 100644 --- a/test/makeup/lexers/elixir_lexer/elixir_lexer_tokenizer_test.exs +++ b/test/makeup/lexers/elixir_lexer/elixir_lexer_tokenizer_test.exs @@ -695,4 +695,22 @@ defmodule ElixirLexerTokenizerTestSnippet do {:punctuation, %{group_id: "group-1"}, "}"} ] end + + describe "custom lexers for sigils" do + test "can register a custom lexer" do + defmodule MyCustomLexer do + def lex(input) do + [{:keyword, %{}, input}] + end + end + + Makeup.Lexers.ElixirLexer.register_sigil_lexer("H", MyCustomLexer) + + assert lex("~H|Hello, world!|") == [ + {:string_sigil, %{}, "~H|"}, + {:keyword, %{}, "Hello, world!"}, + {:string_sigil, %{}, "|"} + ] + end + end end