I am creating a syntax highlighter component using Phoenix.Component and Makeup
defmodule Indy.Bootstrap.Code do
use Phoenix.Component
import Indy.Bootstrap.AssignUtils
def highlight(assigns) do
makeup_code =
Makeup.Lexers.Bootstrap.HTMLLexer.lex(assigns[:code] || "")
|> Makeup.Formatters.HTML.HTMLFormatter.format_inner_as_binary([])
assigns = assign(assigns, :makeup_code, makeup_code)
~H"""
<pre class="chroma"><code class="language-html"><%= Phoenix.HTML.raw(@makeup_code) %></code></pre>
"""
end
end
Using above component, I have to pass code snippet using code
attribute.
<.hightlight code="<h1>This is a heading</h1>"></.highlight>
I want to pass code as inner_block. But I am unable to figure out how to access the inner_block as string.
<.hightlight>
<h1>This is a heading</h1>
</.highlight>
Any pointers or help on how can I achieve this?
Solved using the following code
render_slot(@inner_block)
|> Phoenix.HTML.html_escape()
|> Phoenix.HTML.safe_to_string()
|> Makeup.Lexers.Bootstrap.HTMLLexer.lex()
|> Makeup.Formatters.HTML.HTMLFormatter.format_inner_as_binary([])
|> Phoenix.HTML.raw()
I am using <
and >
as escape characters for embedding live view component code.
<.highlight>
<h1>This is heading</h1>
<p> This is a paragraph</p>
<.alert primary></.alert>
</.highlight>
will render as
I am replacing <
to <
and >
to >
inside Makeup.Lexers.Bootstrap.HTMLLexer.lex
.
Finally code will look something like this.
defmodule Indy.Bootstrap.Code do
use Phoenix.Component
import Indy.Bootstrap.AssignUtils
def highlight(assigns) do
~H"""
<pre class="chroma"><code class="language-html"><%=
render_slot(@inner_block)
|> Phoenix.HTML.html_escape()
|> Phoenix.HTML.safe_to_string()
|> Makeup.Lexers.Bootstrap.HTMLLexer.lex()
|> Makeup.Formatters.HTML.HTMLFormatter.format_inner_as_binary([])
|> Phoenix.HTML.raw() %></code></pre>
"""
end
end
Let me know if you have any other solution or you see problems with this approach.
1 Like
I had a hard time getting the syntax highlighter to work using makeup - had to spend a lot of time understanding makeup
and makeup_html
. I have used syntax highlighters like highlight.js, Pygments, Prism.js in the past.
Pygments
is an inspiration for makeup
- but the output differs between both. makeup_html
lexer generates tokens slightly differently when compared Pygments. I had logged couple of bugs on makeup_html
repository.
I had to modify the default lexer add a function to lex non standard html elements - Makeup.Lexers.Bootstrap.HTMLLexer.lex
.
Leaving these notes here so that others can save some time if they have to highlight code using makeup and phoenix components
2 Likes
I’d suggest moving the formatting out of the heex sigil:
def highlight(assigns) do
formatted =
assigns.inner_block
|> render_slot()
|> Phoenix.HTML.html_escape()
|> Phoenix.HTML.safe_to_string()
|> Makeup.Lexers.Bootstrap.HTMLLexer.lex()
|> Makeup.Formatters.HTML.HTMLFormatter.format_inner_as_binary([])
|> Phoenix.HTML.raw()
assigns = assign(assigns, :formatted, formatted)
~H"""
<pre class="chroma"><code class="language-html"><%= @formatted %></code></pre>
"""
end
Besides being easier to maintain it should also make it clear that the formatted code is not granularly change tracked when used in LiveView.
1 Like
I am afraid the above code does not work - render_slot() macro expansion outside of ~H will fail with the following message:
== Compilation error in file lib/indy_bootstrap/bootstrap/code.ex ==
** (CompileError) lib/indy_bootstrap/bootstrap/code.ex:8: undefined variable "changed" (context Phoenix.LiveView.Engine)
(elixir 1.13.0) expanding macro: Kernel.var!/2
(indy_bootstrap 0.1.0) lib/indy_bootstrap/bootstrap/code.ex:8: Indy.Bootstrap.Code.highlight/1
(phoenix_live_view 0.17.5) expanding macro: Phoenix.LiveView.Helpers.render_slot/1
(indy_bootstrap 0.1.0) lib/indy_bootstrap/bootstrap/code.ex:8: Indy.Bootstrap.Code.highlight/1
(elixir 1.13.0) expanding macro: Kernel.|>/2
(indy_bootstrap 0.1.0) lib/indy_bootstrap/bootstrap/code.ex:13: Indy.Bootstrap.Code.highlight/1
Compiling 1 file (.ex)
Thank you @LostKobrakai I get your point - i had written similar code for other components.