elm-markup is about defining what you're expecting in a markup document.
The elm-markup language relies heavily on indentation, which always some multiple of 4 spaces.
Create a markup Document. You're first goal is to describe a document in terms of the blocks you're expecting.
Here's a brief example of a document that
document : Mark.Document (List String)
document =
Mark.document
identity
(Mark.manyOf
[ Mark.block "MyBlock"
identity
Mark.string
, Mark.string
]
)
parse : String -> Result (DeadEnd Context Problem) (List String)
parse source =
Mark.parse document sourceWhich will parse a document with many of either raw strings orMyBlock blocks. The idenity function usage above means that this will result in a List String, but you could change that function to make this parser result in anything you want!
| MyBlock
Here's text captured in a MyBlock
This text is captured at the top level.
| MyBlock
Here's some more text in a MyBlock.A Block data is just a parser that results in data.
You'll be building up your Document in terms of the Blocks.
A named block.
Mark.block "MyBlock"
Html.text
Mark.stringWill parse the following and render it as Html.text
| MyBlock
Here is an unformatted string!Note block names should be capitalized. In the future this may be enforced.
Parse multiple lines at the current indentation level.
For example:
Mark.block "Poem"
(\str -> str)
Mark.multilineWill parse the following:
| Poem
Whose woods these are I think I know.
His house is in the village though;
He will not see me stopping here
To watch his woods fill up with snow.Where str in the above function will be
"""Whose woods these are I think I know.
His house is in the village though;
He will not see me stopping here
To watch his woods fill up with snow."""Parse an exact string. This can be useful to parse custom types if you pair it with Mark.oneOf.
type Plant = IsWatered | IsDying
Mark.oneOf
[ Mark.exactly "IsWatered" IsWatered
, Mark.exactly "IsDying" IsDying
]An empty block. This could be useful for inserting something that doesn't need parameters.
| LogoExpect a different block based on the result of a previous one.
Handling formatted text is a little more involved than may be initially apparent.
Text styling can be overlapped such as
/My italicized sentence can have *bold*/In order to render this, the above sentence is chopped up into Text fragments that can have multiple styles active.
view is the function to render an individual fragment.inlines are custom inline blocks. These are how links are implemented in Mark.Default!replacements will replace characters before rendering. For example, we can replace ... with the real ellipses unicode character, ….Note check out Mark.Default.text to see an example.
Create a custom inline element.
For example, here is how you could parse a link:
Mark.inline "Link"
(\txt url ->
Html.a [ Html.Attriutes.href url ] (List.map renderText txt)
)
|> Mark.inlineText
|> Mark.inlineString "url"Here's an example of a sentence with the above link:
Here is my sentence {link| with a link to a web-comic. |url=http://www.poorlydrawnlines.com/comic/website-bird/} You're welcome.
Parse an inline String field
Replace a string with another string. This can be useful to have shortcuts to unicode characters.
For example, in Mark.Default, this is used to replace ... with the unicode ellipses character: ….
A balanced replacement. This is used in Mark.Default to do auto-curly quotes.
Mark.balanced
{ start = ( "\"", "“" )
, end = ( "\"", "”" )
}Parse two blocks in sequence.
This can be useful to do things like require that a specific block comes first.
So, the classic case of a blog post that has some meta data at the start could be done like this
Mark.startWith
(\metadata article ->
{ meta = metadata
, article = article
}
)
(Mark.record3 "Article"
(\author title published ->
{ author = author
, title = title
, published = published
}
)
(Mark.field "author" Mark.string)
(Mark.field "title" Mark.string)
(Mark.field "published" Mark.bool)
)
(Mark.manyOf [ Mark.text ])Which would parse the following doc:
| Article
author = Mortimer, the Stylish Elephant
published = False
title = How to use CSS to rearrange your living room.
First off, thank you for using my library.
Secondly, who knew that flex-box could wrap a sofa?It can be useful to parse a tree structure. For example, here's a nested list.
| List
- item one
- item two
- nested item two
additional text for nested item two
- item three
- nested item threeIn order to parse the above, you could define a block as
Mark.block "List"
(\(Nested nested) ->
-- Do something with nested.content and nested.children
)
(Mark.nested
{ item = text
, start = Mark.exactly "-" ()
}
)Note the indentation is always a multiple of 4.
Another Note text in the above code is defined elsewhere.
Embed a data record.
Here's an implementation to render an <img />
Mark.record2 "Image"
(\src description ->
Html.img [ Html.Attribute.src src, Html.Attribute.alt description ] []
)
(Mark.field "src" Mark.string)
(Mark.field "description" Mark.string)Which would parse
| Image
src = http://placekitten/200/500
description = A super cute kitten.Note The order of the fields in the document does not matter, so this will parse correctly as well:
| Image
description = A super cute kitten.
src = http://placekitten/200/500A record with two fields.
Note there's no record1, because that's basically just a block.
Define your own parser using `elm/parser'.
This would be the place to start if you wanted to create a parser for syntax highlighting for an Elm block.
Warning This is the only place where you can break the rules/conventions of elm-markup.
Note You can get the current expected indentation using Parser.Advanced.getIndent.