elm-markupはelm-uiの作者が作ったカスタマイズできるマークアップのパーサーです
ちょっと可能性を感じてしまったので触ってみました
まずはデモアプリを用意したので
| Counter
と入れてきてください
こんな感じにカウンターが表示されるはずです
ちなみにこのカウンターは普通に動きます。(複数個置いても状態は共有されてるので一緒にカウントされます)
こんな感じに独自コンポーネントをマークアップを書くだけで表示する力がelm-markupにはあります!
何でおもしろいと思っているのか
- JAM stack的な流れに乗りたい
- ノベルエンジンとかシナリオ作成に使えそう
型
何はともあれ型を見よと神もおっしゃられておる
type Block data
type Document result
document : (child -> result) -> Block child -> Document result
parse :
Document result
-> String
-> Result (List (DeadEnd Context Problem)) result
Document型はdocument
でBlockから上げないと作れません。なので自分でカスタマイズする際はBlock型を使って、できたDocumentをparse
に食わします
Document型はパーサーを表しているような?こういう方式のことをなんていうかわからないので、誰か知ってたら教えてください
Document result
resultは結果の型です
これが型変数だということはelm-markupでパースした結果はどんな型にでもできるということです!
マークダウンは普通、HTMLになるだけですがelm-markupでは任意の型に変換できます
今回のサンプルではDocument (Model -> Element Msg)
型にしています
parse結果にModelを入れれるのでサンプルのように動的なカウンターが埋め込めるわけですね
Block a
Mark.boolとかstring, intのような基本的なものが用意されてます
(BlockというかParserな気がするんですが)
Mark.blockがカスタマイズできるやつです
| MyBlock
body
こんな感じのマークアップを
Mark.block "MyBlock" Mark.string
これでパースできます。これはBlock String
になるので適宜mapしてください
| Hoge
がブロックなんですが、型のBlockとは意味の範囲が違うので気を付けましょう。型のほうはParser型みたいな感じです
Record
上の例じゃおもしろくないのでレコードの例を見てみます
マークダウンみたいに画像表示用のブロックを作りましょう
| Image
src = http://placekitten/200/500
description = A super cute kitten.
Imageブロックにsrcとdescriptionのデータが足されています。これを使って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)
Mark.recordN
を使うとフィールドを渡すことができるようになります
名前付き引数を渡せる感じになりますね
マークアップ上は順不同で書けます。つまりsrcとdescriptionはどっちを先に書いてもいいです
インデントはスペース4です。elm-markupはインデントにはセンシティブなので気を付けましょう
リストもマークアップできます
- いわゆるこれ
- マークダウンで一番使うやつ
- 当者調べです
- ネストもします
- elm-markupでもできます
nested :
{ item : Block item
, start : Block icon
}
-> Block (List (Nested ( icon, List item )))
type Nested item
= Nested
{ content : item
, children : List (Nested item)
}
中身のパーサー(item)と先頭要素のパーサー(start)を渡したらパースできるみたいですね。
まだ試してないです
Mark.text
Mark.textが本文を主にパースしてくれます
text :
{ view : Text -> rendered
, inlines : List (Inline rendered)
, replacements : List Replacement
}
-> Block (List rendered)
textは何も設定しなくても*itaric*, ~strike~, *bold*
がパースされてstyleとして渡されます
type Text
= Text (List Style) String
type Style
= Bold
| Italic
| Strike
Text型を自分が欲しい型に変換すればいいです
replacementsは文字を他の文字に置き換えます
inlinesは大事なインラインブロック記法の定義になります
インラインブロック
マークダウンでよく使う記法第2位のリンクはインラインブロックで定義できます
{link| with a link to a web-comic. |url=http://www.poorlydrawnlines.com/comic/website-bird/}
これをパースします。|
で分かれていて最初がブロックの名前、次が本文、最後がパラメータ、といった形になります
Mark.inline "link"
(\txt url ->
Html.a [ Html.Attriutes.href url ] (List.map renderText txt)
)
|> Mark.inlineText
|> Mark.inlineString "url"
実は試してないので挙動はよくわかってないです。3分割じゃなくて任意の数な気はしますね
inlineInt
とかはないので受けれるのはStringだけのようです。ちょっと謎ですね
組み合わせ
あとは今まででてきたものを組み合わせましょう
oneof
oneOf : List (Block a) -> Block a
これはよくあるやつですね
最初に成功したBlockを使います
startWith
startWith : (start -> rest -> result) -> Block start -> Block rest -> Block result
必ずTitleブロックから始まる文書とかに使えます。
manyOf
manyOf : List (Block a) -> Block (List a)
Many blocks that are all at the same indentation level.
という説明しか書いてないですが、manyOfは絶対に使う関数です!
今まで出てきたBlockは1行しか読み込みません。複数行の文章を扱いたいときはmanyOf
でBlock (List a)
にしてもらわなきゃいけません。このListが行にあたります
行が固定値の場合はstartWith
を行数分つなげればできますが、あんまりないと思うので必ずmanyOf
を使うことになると思います
注意:
manyOf [ manyOf [], ... ]
このように直接ネストするとエラーになるかもしれません。また原因分かったら追記かなんかします
Mark.multiline
を動かすとElmアプリケーション全体が止まります。たぶん。
たぶん無限ループしてると思うんですがよくわかりません。
どのみち存在意義はあんまりないので使わなければいいと思います
終わり
自分でも何を書いたらいいかわかんなかったんですけど、elm-markupがおもしろいと思ったので書きました
今後はいきなりノベルエンジンなるものを作ってみよう!とかいうと挫折するので小さめに作れる題材を考えたいと思います