RobertDober

RobertDober

Earmark - Elixir's Markdown Converter

Earmark is a pure-Elixir Markdown converter.

It is intended to be used as a library (just call Earmark.as_html), but can also be used as a command-line tool (run mix escript.build first).

Output generation is pluggable.

239 11852 134

Most Liked

kip

kip

ex_cldr Core Team

@RobertDober thanks for all your work on Earmark, its a critical part of the ecosystem and you’ve made it better through your efforts.

MarioFlach

MarioFlach

I started experimenting with the AST. Pretty straight forward for what I want to do:

defmodule GitGud.Web.Markdown do
  @moduledoc """
  Conveniences for rendering Markdown.
  """

  @doc """
  Renders a Markdown formatted `content` to HTML.
  """
  @spec markdown(binary | nil) :: binary | nil
  def markdown(nil), do: nil
  def markdown(content) do
    case Earmark.as_ast(content) do
      {:ok, ast, _warnings} ->
        ast
        |> transform_ast()
        |> Floki.raw_html()
    end
  end

  #
  # Helpers
  #

  defp transform_ast(ast) do
    ast
    |> Enum.map(&transform_ast_node/1)
    |> List.flatten()
  end

  defp transform_ast_node({tag, _attrs, _ast} = node) when tag in ["code"], do: node
  defp transform_ast_node({tag, attrs, ast}) do
    {tag, attrs, transform_ast(ast)}
  end

  defp transform_ast_node(content) when is_binary(content) do
    content = Regex.replace(~r/:([a-z0-1\+]+):/, content, &emojify_short_name/2)
    auto_link(content, Regex.scan(~r/#[0-9]+|@[a-zA-Z0-9_-]+|[a-f0-9]{7}/, content, return: :index))
  end

  defp emojify_short_name(match, short_name) do
    if emoji = Exmoji.from_short_name(short_name),
     do: Exmoji.EmojiChar.render(emoji),
   else: match
  end

  defp auto_link(content, []), do: content
  defp auto_link(content, indexes) do
    {content, rest, _offset} =
      Enum.reduce(List.flatten(indexes), {[], content, 0}, fn {idx, len}, {acc, rest, offset} ->
        {head, rest} = String.split_at(rest, idx - offset)
        {link, rest} =
          case String.split_at(rest, len) do
            {"#" <> number, rest} ->
              {{"a", [], ["##{number}"]}, rest} # TODO
            {"@" <> login, rest} ->
              {{"a", [{"class", "has-text-black"}], ["@#{login}"]}, rest} # TODO
            {hash, rest} ->
              {{"a", [], [{"code", [{"class", "has-text-link"}], [hash]}]}, rest} # TODO
          end
        {acc ++ [head, link], rest, idx+len}
      end)
    List.flatten(content, [rest])
  end
end
RobertDober

RobertDober

I just released Earmark 1.4.6

  • Exposes the now stable AST in the quadruple format.

  • There are many important bug fixes, all known crashes have been fixed, some issues like code blocks in lists and better HTML support did not make it.

Thanks to all of you for your great bug reporting after 1.4.5 which was a rough one.

Here go the Release Notes:

RobertDober

RobertDober

I have just released, what I consider my last release of

Earmark 1.4.41 2023-09-19 Palindrome Edition

Isolate from EarmarkParser in order to prevent conflicts with indirect dependencies
on EarmarkParser via ExDoc.
This shall mark the end of my role as maintainer of Earmark.

Rationale

Removing the dependency on EarmarkParser allows to

  • let the maintainer of Earmark decide how to parse markdown
  • avoid indirect version conflicts or problems for libs using EarmarkEarmarkParser and ex_docEarmarkParser as has occurred here
RobertDober

RobertDober

I have just released EarmarkParser v1.4.8

I always wanted to release a new library starting with that high a version :wink:

actually I will very soon use EarmarkParser in Earmark

and EarmarkParser is just Earmark without HTML Transfomation and CLI, thusly implementing only what is needed for ex_doc.

I had the following motivations for this step

  • Why installing unused code in all libraries that use ex_doc?

  • Allow Earmark to work on configurable transformers (maybe also other formats)

  • Allow Earmark to use libraries which use ex_doc as soon as ex_doc agrees to use EarmarkParser

Here is the Release Note

EarmarkParser

1.4.8 2020/06/29

This marks the first release of the parser isolated from the rest of Earmark.

It is feature identical to the 1.4.7 release of Earmark.

All releases below were Earmark, all releases above are only EarmarkParser.

Earmark

1.4.7 2020/06/29

Where Next?

Popular in Libraries Top

kip
Image is an image processing library for Elixir. It is based upon the fabulous vix library that provides a libvips wrapper for Elixir. I...
574 16576 179
New
Crowdhailer
Raxx is an alternative to Plug and is inspired by projects such as Rack(Ruby) and Ring(Clojure). 1.0-rc.1 is now available. To use it re...
New
tompave
Hello there, I would like to share a feature toggles library (AKA feature flags) I’ve been working on. The main package is FunWithFlags...
New
woylie
I released Doggo, a collection of unstyled Phoenix components. Features Unstyled Phoenix components. Storybook that can be added to...
New
archan937
It is a well-know topic within the Elixir community: “To mock or not to mock? :)” Every alchemist probably has his / her own opinion con...
New
woutdp
Hi! I wanted to introduce my latest project LiveSvelte. It allows you to render Svelte inside LiveView with end-to-end reactivity. It’s ...
New
blatyo
https://www.conduitframework.com/ The best overview for how things are tied together is this presentation. Modules and functions are pre...
New
wfgilman
I’ve cleaned up and open sourced three financial libraries I was using for my company. They are bindings for the APIs of these three comp...
New
benlime
LiveMotion enables high performance animations declared on the server and run on the client. As a follow up to my previous thread A libr...
New
gjaldon
As the title states, EctoEnum has just been updated after some time of hardly any activity in the repo. Here’s the latest release: https:...
New

Other popular topics Top

dotdotdotPaul
Okay, I'm having a heck of a time trying to figure out how to best handle the validation of belongs_to associations in Ecto. I'm sure I'...
New
jerry
Good day to you all. I have been struggling to get a query involving like and ilike to work. Can anyone assist me on this, please? pro...
New
sergio
I couldn’t find any guides that worked well with Phoenix 1.6.0 and esbuild. I hope this helps people test the waters and eases you into t...
New
Harrisonl
We have an ECS cluster with 4 services, where each task joins a single cluster, via discovery ECS discovery service. Currently when I de...
New
fireproofsocks
I’m working on defining a simple Ecto schema for a table (in PostGres), but I don’t see where I can define a column as NOT NULL. Conside...
New
shahryarjb
Hello, I have map which I want to convert it to string like this: the map: %{last_name: "tavakkoli", name: "shahryar"} the string I ne...
New
msaraiva
Surface is an experimental library built on top of Phoenix LiveView and its new LiveComponent API that aims to provide a more declarative...
564 42633 214
New
Patoshizzle
After calling mix ecto.create I get this error: 17:00:32.162 [error] GenServer #PID&lt;0.412.0&gt; terminating ** (Postgrex.Error) FATAL...
New
belgoros
I’m not a pro in using Regex and can’t figure out why the following behaviour happens, especially if we take into account the difference ...
New
lanycrost
Hi everyone! I need implement if…else if…else condition from my elixir code, and anymore of this control flow structures not work proper...
New

Sub Categories:

We're in Beta

About us Mission Statement