Markdown limits headings to level 6 or less. HTML does this, too (<h7> is not supported by most browsers).
This type represents the possible heading levels that a Markdown heading block may have.
If you do a heading level above 6, the # characters will be treated as literal #'s.
A convenience function so that you don't have to write a big case statement if you need a heading level as an Int.
The value for an unordered list item, which may contain a task.
A task (or no task), which may be contained in a ListItem.
Alignment in a header cell in a markdown table. See the Table variant in the Block type.
The way HTML is handled is one of the core ideas of this library.
You get the full HTML structure that you can use to process the Blocks before rendering them. Once you render them, all of the raw text within your HTML is parsed as Markdown.
HtmlComments and metadataHtmlComments contain the raw comment text, completely unprocessed. That means
you'll need to trim it if you want to strip the leading or trailing whitespace.
Renderer's do not process HtmlComments. If you want to do any special processing
based on HTML comments, you can inspect the Markdown.Block.Blocks before rendering
it and perform any special processing based on that. You could even add or remove
Blocks, for example, based on the presence of certain comment values.
See <Markdown.Html> for more.
An Inline block. Note that HtmlInlines can contain Blocks, not just nested Inlines.
An Html attribute. In <div class="foo">, you would have { name = "class", value = "foo" }.
Extract the text from a list of inlines.
inlines : List (Inline)
inlines =
[ Text "Heading with "
, Emphasis 1
[ Text "emphasis" ]
]
extractText inlines == "Heading with emphasis"
-- Original string: "Heading with *emphasis*"Recursively apply a function to transform each Block.
This example bumps headings down by one level.
import Markdown.Block as Block exposing (..)
bumpHeadingLevel : HeadingLevel -> HeadingLevel
bumpHeadingLevel level =
case level of
H1 -> H2
H2 -> H3
H3 -> H4
H4 -> H5
H5 -> H6
H6 -> H6
[ Heading H1 [ Text "First heading" ]
, Paragraph [ Text "Paragraph" ]
, BlockQuote
[ Heading H2 [ Text "Paragraph" ]
]
, Heading H1 [ Text "Second heading" ]
]
|> List.map
(Block.walk
(\block ->
case block of
Heading level children ->
Heading (bumpHeadingLevel level) children
_ ->
block
)
)
--> [ Heading H2 [ Text "First heading" ]
--> , Paragraph [ Text "Paragraph" ]
--> , BlockQuote
--> [ Heading H3 [ Text "Paragraph" ]
--> ]
--> , Heading H2 [ Text "Second heading" ]
--> ]import Markdown.Block as Block exposing (..)
[ Paragraph
[ Link "http://elm-lang.org" Nothing [ Text "elm-lang homepage" ]
]
]
|> List.map
(Block.walkInlines
(\inline ->
case inline of
Link destination title inlines ->
Link (String.replace "http://" "https://" destination) title inlines
_ ->
inline
)
)
--> [ Paragraph
--> [ Link "https://elm-lang.org" Nothing [ Text "elm-lang homepage" ]
--> ]
--> ]Apply a function to transform each inline recursively.
If any of the values are Errs, the entire value will be an Err.
import Markdown.Block as Block exposing (..)
lookupLink : String -> Result String String
lookupLink key =
case key of
"elm-lang" ->
Ok "https://elm-lang.org"
_ ->
Err <| "Couldn't find key " ++ key
resolveLinkInInline : Inline -> Result String Inline
resolveLinkInInline inline =
case inline of
Link destination title inlines ->
destination
|> lookupLink
|> Result.map (\resolvedLink -> Link resolvedLink title inlines)
_ ->
Ok inline
[ Paragraph
[ Link "angular" Nothing [ Text "elm-lang homepage" ]
]
]
|> Block.validateMapInlines resolveLinkInInline
--> Err [ "Couldn't find key angular" ]Map values, while also tracking state while traversing every block. Think of it as a helper for foldl and map
in a single handy function!
In this example, we ne need to keep track of the number of occurrences of a heading name so that we can use a unique slug to link to (exactly like Github does for its heading links). We keep the occurences in a Dict, so this allows us to maintain state rather than just transforming blocks purely based on the current block.
You can see the full end-to-end code for this in examples/src/Slugs.elm.
import Markdown.Block as Block exposing (..)
import Dict
gatherHeadingOccurrences : List Block -> ( Dict.Dict String Int, List ( Block, Maybe String ) )
gatherHeadingOccurrences =
Block.mapAndAccumulate
(\soFar block ->
case block of
Heading level inlines ->
let
inlineText : String
inlineText =
Block.extractInlineText inlines
occurenceModifier : String
occurenceModifier =
soFar
|> Dict.get inlineText
|> Maybe.map String.fromInt
|> Maybe.withDefault ""
in
( soFar |> trackOccurence inlineText
, ( Heading level inlines, Just (inlineText ++ occurenceModifier) )
)
_ ->
( soFar
, ( block, Nothing )
)
)
Dict.empty
trackOccurence : String -> Dict.Dict String Int -> Dict.Dict String Int
trackOccurence value occurences =
occurences
|> Dict.update value
(\maybeOccurence ->
case maybeOccurence of
Just count ->
Just <| count + 1
Nothing ->
Just 1
)
[ Heading H1 [ Text "foo" ]
, Heading H1 [ Text "bar" ]
, Heading H1 [ Text "foo" ]
]
|> gatherHeadingOccurrences
--> ( Dict.fromList
--> [ ( "bar", 1 )
--> , ( "foo", 2 )
--> ]
--> , [ ( Heading H1 [ Text "foo" ], Just "foo" )
--> , ( Heading H1 [ Text "bar" ], Just "bar" )
--> , ( Heading H1 [ Text "foo" ], Just "foo1" )
--> ]
--> )Fold over all blocks to yield a value.
import Markdown.Block as Block exposing (..)
maximumHeadingLevel : List Block -> Maybe HeadingLevel
maximumHeadingLevel blocks =
blocks
|> Block.foldl
(\block maxSoFar ->
case block of
Heading level _ ->
if Block.headingLevelToInt level > (maxSoFar |> Maybe.map Block.headingLevelToInt |> Maybe.withDefault 0) then
Just level
else
maxSoFar
_ ->
maxSoFar
)
Nothing
[ Heading H1 [ Text "Document" ]
, Heading H2 [ Text "Section A" ]
, Heading H3 [ Text "Subsection" ]
, Heading H2 [ Text "Section B" ]
]
|> maximumHeadingLevel
--> (Just H3)
This is the AST (abstract syntax tree) that represents your parsed markdown.
In the simplest case, you can pass this directly to a renderer: