Skip to content

Commit

Permalink
Allow to register custom lexers for sigils (#34)
Browse files Browse the repository at this point in the history
This allows highlighting HEEx sigils using the HEExLexer, for example.
  • Loading branch information
SteffenDE authored Nov 6, 2024
1 parent d83f23f commit 8b06e03
Show file tree
Hide file tree
Showing 2 changed files with 64 additions and 0 deletions.
46 changes: 46 additions & 0 deletions lib/makeup/lexers/elixir_lexer.ex
Original file line number Diff line number Diff line change
Expand Up @@ -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)]

Expand Down Expand Up @@ -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
18 changes: 18 additions & 0 deletions test/makeup/lexers/elixir_lexer/elixir_lexer_tokenizer_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -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

0 comments on commit 8b06e03

Please sign in to comment.