pablohirafuji / elm-markdown / Markdown.Block

Block parsing, rendering and helpers.

Model


type Block b i
    = BlankLine String
    | ThematicBreak
    | Heading String Basics.Int (List (Markdown.Inline.Inline i))
    | CodeBlock CodeBlock String
    | Paragraph String (List (Markdown.Inline.Inline i))
    | BlockQuote (List (Block b i))
    | List ListBlock (List (List (Block b i)))
    | PlainInlines (List (Markdown.Inline.Inline i))
    | Custom b (List (Block b i))

The block type.


type CodeBlock
    = Indented
    | Fenced Basics.Bool Fence

CodeBlock type.


type alias Fence =
{ indentLength : Basics.Int
, fenceLength : Basics.Int
, fenceChar : String
, language : Maybe String 
}

Code fence model.


type alias ListBlock =
{ type_ : ListType
, indentLength : Basics.Int
, delimiter : String
, isLoose : Basics.Bool 
}

List model.


type ListType
    = Unordered
    | Ordered Basics.Int

Types of list.

Parsing

parse : Maybe Markdown.Config.Options -> String -> List (Block b i)

Turn a markdown string into a list of blocks.

blocks : List (Block b i)
blocks =
    parse Nothing "# Heading with *emphasis*"

It's the same of:

blocks : List (Block b i)
blocks =
    [ Heading "Heading with *emphasis*"
        1
        [ Text "Heading with "
        , Emphasis 1
            [ Text "emphasis" ]
        ]
    ]

Note: If Maybe Options is Nothing, Config.defaultOptions will be used.

Rendering

toHtml : Block b i -> List (Html msg)

Transform a Block into a list of Html using the default html elements.

import Html exposing (Html, div)
import Markdown.Block as Block

view : Html msg
view =
    myMarkdownString
        |> Block.parse Nothing
        -- using Config.defaultOptions
        |> List.map Block.toHtml
        |> List.concat
        |> div []

defaultHtml : Maybe (Block b i -> List (Html msg)) -> Maybe (Markdown.Inline.Inline i -> Html msg) -> Block b i -> List (Html msg)

If you want to customize the html output, this function will help you.

Transform a block into a list of Html, optionally using custom html elements to render inner blocks or/and inlines.

Example of rendering:

import Html exposing (..)
import Html.Attributes exposing (..)
import Markdown.Block as Block exposing (Block(..))
import Markdown.Inline as Inline exposing (Inline(..))

view : Html msg
view =
    myMarkdownString
        |> Block.parse Nothing
        -- using Config.defaultOptions
        |> List.map customHtmlBlock
        |> List.concat
        |> article []

customHtmlBlock : Block b i -> List (Html msg)
customHtmlBlock block =
    case block of
        BlockQuote blocks ->
            List.map customHtmlBlock blocks
                |> List.concat
                |> details []
                |> (\a -> (::) a [])

        _ ->
            Block.defaultHtml
                (Just customHtmlBlock)
                (Just customHtmlInline)
                block

customHtmlInline : Inline i -> Html msg
customHtmlInline inline =
    case inline of
        Image url maybeTitle inlines ->
            figure []
                [ img
                    [ alt (Inline.extractText inlines)
                    , src url
                    , title (Maybe.withDefault "" maybeTitle)
                    ]
                    []
                , figcaption []
                    [ text (Inline.extractText inlines) ]
                ]

        Link url maybeTitle inlines ->
            if String.startsWith "http://elm-lang.org" url then
                a
                    [ href url
                    , title (Maybe.withDefault "" maybeTitle)
                    ]
                    (List.map customHtmlInline inlines)
            else
                a
                    [ href url
                    , title (Maybe.withDefault "" maybeTitle)
                    , target "_blank"
                    , rel "noopener noreferrer"
                    ]
                    (List.map customHtmlInline inlines)

        _ ->
            Inline.defaultHtml (Just customHtmlInline) inline

Note: If both Maybe arguments are Nothing, the default html elements will be used to render the inner blocks and inlines.

Helpers

walk : (Block b i -> Block b i) -> Block b i -> Block b i

Apply a function to every block whithin a block recursively.

Example of replacing all level 3+ heading to regular paragraphs:

import Html exposing (Html, section)
import Markdown.Block as Block exposing (Block(..))

view : Html msg
view =
    myMarkdownString
        |> Block.parse Nothing
        -- using Config.defaultOptions
        |> List.map (Block.walk modHeader)
        |> List.map Block.toHtml
        |> List.concat
        |> section []

modHeader : Block b i -> Block b i
modHeader block =
    case block of
        Heading rawText level inlines ->
            if level >= 3 then
                Paragraph rawText inlines
            else
                block

        _ ->
            block

walkInlines : (Markdown.Inline.Inline i -> Markdown.Inline.Inline i) -> Block b i -> Block b i

Apply a function to every block's inline recursively.

Example of converting all Text to UPPERCASE:

import Html exposing (Html, section)
import Markdown.Block as Block exposing (Block(..))
import Markdown.Inline exposing (Inline(..))

view : Html msg
view =
    myMarkdownString
        |> Block.parse Nothing
        |> List.map (Block.walkInlines upperText)
        |> List.map Block.toHtml
        |> List.concat
        |> section []

upperText : Inline i -> Inline i
upperText inline =
    case inline of
        Text str ->
            Text (String.toUpper str)

        _ ->
            inline

query : (Block b i -> List a) -> Block b i -> List a

Walks a block and applies a function for every block, appending the results.

Example of getting all headings of a list of blocks:

toc : List ( Int, String )
toc =
    myMarkdownString
        |> Block.parse Nothing
        |> List.map (Block.query getHeader)
        |> List.concat

getHeader : Block b i -> List ( Int, String )
getHeader block =
    case block of
        Heading _ lvl inlines ->
            [ ( lvl, Inline.extractText inlines ) ]

        _ ->
            []

queryInlines : (Markdown.Inline.Inline i -> List a) -> Block b i -> List a

Walks a block and applies a function for every inline, appending the results.

Example of getting all links within a list of blocks:

links : List String
links =
    myMarkdownString
        |> Block.parse Nothing
        |> List.map (Block.queryInlines getLinks)
        |> List.concat

getLinks : Inline i -> List String
getLinks inline =
    case inline of
        Link url _ _ ->
            [ url ]

        _ ->
            []