leandrocp

leandrocp

MDEx - Fast and Extensible Markdown

MDEx is a fast and extensible Markdown parser and formatter.

Fast

Leverage Rust to parse, manipulate and render documents using:

Extensible

A Req-like API to manipulate documents in a pipeline with support for plugins, for eg mdex_mermaid

Features

More at GitHub - leandrocp/mdex: Fast and Extensible Markdown for Elixir. Compliant with CommonMark spec. Formats to HTML, JSON, and XML. Built on top of comrak, ammonia, and autumnus.

Most Liked

leandrocp

leandrocp

MDEx v0.9 is now available with some new cool features!

Delta format

Thanks to @Jskalc we can now convert Markdown to Delta:

Streaming

Initial (experimental) support to put incomplete fragments of Markdown into the document. Useful for AI/LLM tools:

You can see a demo (video) at https://x.com/leandrocesquini/status/1974460068054843575

Inspect Document as a tree

To help visualize the nodes in a document:

It can be disabled if you prefer the regular struct inspect.

Unified Document API

Removed the MDEx.Pipe module in favor of unifying all operations in the MDEx.Document. Note this is a breaking change:

I’ll be working on Support Phoenix tags? · Issue #571 · kivikakk/comrak · GitHub so we can (hopefully) get Elixir and Phoenix components rendering inside Markdown. Enjoy!

leandrocp

leandrocp

MDEx v0.7.0 is out with a new ~MD sigils supporting assigns and Elixir expressions:

import MDEx.Sigil

assigns = %{lang: "elixir", sample: "spawn(fn -> send(current, {self(), 1 + 2}) end)"}

~MD"""
## Lang: <%= String.capitalize(@lang) %>

```elixir
<%= @sample %>
```
"""

Outputs:

%MDEx.Document{
  nodes: [
    %MDEx.Heading{nodes: [%MDEx.Text{literal: "Lang: Elixir"}], level: 2, setext: false},
    %MDEx.CodeBlock{
      nodes: [],
      fenced: true,
      fence_char: "`",
      fence_length: 3,
      fence_offset: 0,
      info: "elixir",
      literal: "spawn(fn -> send(current, {self(), 1 + 2}) end)\n"
    }
  ]
}

Or using the HTML modifier:

"<h2>Lang: Elixir</h2>\n<pre class=\"athl\" style=\"color: #abb2bf; background-color: #282c34;\"><code class=\"language-elixir\" translate=\"no\" tabindex=\"0\"><span class=\"line\" data-line=\"1\"><span style=\"color: #61afef;\">spawn</span><span style=\"color: #848b98;\">(</span><span style=\"color: #c678dd;\">fn</span> <span style=\"color: #abb2bf;\">-&gt;</span> <span style=\"color: #61afef;\">send</span><span style=\"color: #848b98;\">(</span><span style=\"color: #abb2bf;\">current</span><span style=\"color: #848b98;\">,</span> <span style=\"color: #848b98;\">&lbrace;</span><span style=\"color: #61afef;\">self</span><span style=\"color: #848b98;\">(</span><span style=\"color: #848b98;\">)</span><span style=\"color: #848b98;\">,</span> <span style=\"color: #d19a66;\">1</span> <span style=\"color: #abb2bf;\">+</span> <span style=\"color: #d19a66;\">2</span><span style=\"color: #848b98;\">&rbrace;</span><span style=\"color: #848b98;\">)</span> <span style=\"color: #c678dd;\">end</span><span style=\"color: #848b98;\">)</span>\n</span></code></pre>"

That’s one step into the direction of supporting Markdown in LiveViews with components.

leandrocp

leandrocp

Hello! One more update. Now mdex supports code block decorators, ie: you can add metadata in the code fence info string to overwrite theme, highlight colors, etc. See examples below and docs at MDEx — MDEx v0.10.0

leandrocp

leandrocp

Very interesting package with lots of features that took my attention!

Thanks!

  1. It would be nice to see a comparison table.

Done. Here’s a comparison table and a livebook to compare the output of some markdown libraries.

  1. Would it be hard to add support for a custom markdown rules or even custom sets of rules

Hard to tell if it’s easier or harder, it depends on your needs, but I’d argue it tends to be easier. For instance here’s the code to render Mermaid graphs and here are some examples - you’ll notice it’s all about transforming a tree of nodes. MDEx use structs for a couple of reasons but it’s not much different than earmark_parser ast or even floki ast.

For example see how many interesting features adds ex_doc to the markdown.

Many features on ex_doc are actually implemented on the HTML data structure/AST either using external libraries to perform operations like syntax highlight or emoji (both native to MDEx) or to autolink module/function. So technically any other Markdown library could implement ExDoc.Markdown and all the rest would just work, although I’ve never tried it :smile:

  1. Would it be hard to add support for Earmark (and other packages)? Imagine a case where there is a critical bug in the Rust parser - for Elixir app it means stopping entire app.

I’m not sure I follow this question. Do you mean as a backend for MDEx? If so, then no because Earmark doesn’t fully support CommonMark which was a requirement for MDEx in the first place. And comrak is pretty stable, the author is very responsive (even contributed to MDEx already) and it’s used by many libraries in the Rust ecosystem including for example to build the Deno documentation.

writing parser is not trivial

Nope it’s not. Markdown is way more complex than it looks but all the credit on the parser goes to comrak and cmark-gfm.

Maybe this is not the same kind of feedback you are interested

That was super useful, really good topics. Let me know if something is not clear.

leandrocp

leandrocp

Thanks! Yep you can create either provide your own %Autumn.Theme{} or modify existing ones, for example:

%{highlights: highlights} = theme = Autumn.Theme.get("github_dark")
red_text_style = %Autumn.Theme.Style{fg: "red"}
highlights = Map.put(highlights, "string", red_text_style)
theme = Map.put(theme, :highlights, highlights)

Then format using that theme:

MDEx.to_html!("""
```elixir
String.upcase("elixir")
```
""",
syntax_highlight: [formatter: {:html_inline, theme: theme}])

You’ll see <span style="color: red;">&quot;elixir&quot;</span> in the output.

I should probably implement the Access behaviour to let you just call put_in(theme, [...], value) :laughing:

EDIT: Autumn is another library, you can find docs at Autumn — Autumn v0.3.3

Where Next?

Popular in Announcing Top

ityonemo
Currently just starting out on a new mini-project - getting zig NIFs to run in elixir. https://github.com/ityonemo/zigler The idea here...
New
RobinBoers
Renders Jinja templates, either from disk or defined at runtime, with support for all native Jinja features (including extends). It does...
New
rodloboz
I’ve started working on a new library to run SQL queries and do basic business intelligence. Think “Blazer for Elixir.” Currently it fe...
New
frerich
An application wishing to store larger amounts of data typically has two options for doing so: A new column on some table can be introd...
New
yotor_coder
:glowing_star: Looking for Contributors to Join PyraUI Hey everyone :waving_hand: I’ve been working on PyraUI — a modern UI component li...
New
lucaong
CubDB is an embedded database written in pure Elixir, designed for robustness and minimal use of resources. It strives to be as developer...
New
wingyplus
I just did a dirty hack after seeing Zoi on x.com a few hours ago. Quick Introduction The zoi_defstruct is a library to help you generat...
New
mikehostetler
LLM DB - LLM Model Metadata Package This package was extracted out of the ReqLLM project. LLM DB is a model metadata catalog with fast, ...
New
rms.mrcs
Hi there :waving_hand: Just dropping by to share PhonixLiveState, a lib I’ve just published. It’s still VERY RAW, but already in a reas...
New
anuaralfetahe
Hello Published a new library - ProcessHub! ProcessHub is a library designed to manage process distribution within the Elixir cluster. ...
New

Other popular topics Top

yurko
Here are few pieces of (common) Linux knowledge that we use for reasonably small one server apps. We use Ubuntu but this should work for ...
New
New
lastday4you
I wanted to check elixir version in phoenix because i found that my elixir is 1.5 but when i use Enum.chunk_by it said the function is un...
New
script
If I have a string “1000 cfu/ml” . I want to remove the characters and / and space . So the string is like this "1000" What is the ...
New
stefanchrobot
What’s the safe way to decode a JSON string into a struct? I want to avoid calling String.to_atom. Jason.decode can give me a map with st...
New
vonH
When I run the Plug and I recompile I wind up having to use Ctrl C to quit iex and start again. Witht the help of rlwrap I can use the cu...
New
johnnyicon
Hi all, I've just started learning Elixir and Phoenix Framework, so please pardon my n00bness at this stage. I'm trying to use Postg...
New
Qqwy
Original source of discussion: This topic on the Pragmatic Programmers' Functional Web Development with Elixir, OTP, and Phoenix forum. ...
New
AstonJ
We’ve put together this wiki for Phoenix LiveView - please feel free to add any info you feel is worth including. What is Phoenix LiveV...
New
romenigld
I am trying to run a deploy with docker and I successfully runned with this command: docker build -t romenigld/blog-prod . but when I t...
New

We're in Beta

About us Mission Statement