misskey-dev / mfm.js Goto Github PK
View Code? Open in Web Editor NEWAn MFM parser implementation with TypeScript.
License: MIT License
An MFM parser implementation with TypeScript.
License: MIT License
InlineParserを適用して受けられる恩恵が少ない
PlainParserで十分
メンションと解釈しなくていい部分で解釈される。
メンションによる間違ったリンクが生成される可能性がある。
[GitHub (@marihachi)](https://github.com/marihachi)
[
{
"type": "link",
"props": {
"silent": false,
"url": "https://github.com/marihachi"
},
"children": [
{
"type": "text",
"props": {
"text": "GitHub ("
}
},
{
"type": "mention",
"props": {
"username": "marihachi",
"host": null,
"acct": "@marihachi"
}
},
{
"type": "text",
"props": {
"text": ")"
}
}
]
}
]
rule = "var" _ @innerRule
innerRule = [a-z]i
とすることで、
rule = "var" _ [a-z]i
のように展開されるらしい(要確認)。
特に拘りがなければ、分かりやすさのためそうしたい
将来的にはStable版とCanary版をnpmにリリースしていきたい。
普通どんな感じでブランチの運用とかリリースをするんだろう。
うまくGitHub Actionsも使えると管理が楽そう
関数構文は2種類ある。
構文A: [fn content]
構文B: $[fn content]
どちらかの構文に統一したほうが良いと思う。
キャッシュ(packrat parsing)のおかげで、構文Aでもそこまで負荷が高くならなくなった。
構文A [fn content]
の廃止を予定。
Related to #49
HTMLタグのmarqueeに相当する構文が欲しいです。モダンにCSS3で実装しちゃうか、雑にタグ置くか、どっちが良いんでしょうか
Want to back this issue? Post a bounty on it! We accept bounties via Bountysource.
peg.jsは今後更新されないと思われるので、forkのPeggyに移行するかも。
https://github.com/peggyjs/peggy
<center>
a
b
</center>
そっちの方が実装に即してそう。あと他のライブラリでも類似の処理は traverse と命名されることが多い
https://ja.wikipedia.org/wiki/%E3%83%88%E3%83%A9%E3%83%90%E3%83%BC%E3%82%B9
コンピュータソフトウェアでは、木構造やグラフの全てのノードを辿ること。
リンクと記号が被っていてパース時の負荷に繋がる。あと[foo bar]
のような文字列は意図せず現れる場合も多いため、関数構文が誤発動してしまうこともしばしばみられる。そのため構文を変更したい
太字構文でも、イタリック構文のように前の文字が改行やスペースの時のみ太字構文として判定するようにしたい。
**bold**
__bold__
高負荷にならないように
```
a
```
はコードブロックとして認識するが、
```
a
a
```
は認識しない
ASTツリーの全textノードにnyaizeとかしたいよね
Related to #30
ブロックの前後にある改行を消費するようにする
<nomfm></nomfm>
とか
Want to back this issue? Post a bounty on it! We accept bounties via Bountysource.
C#の後ろに来る文章がハッシュタグとして認識されないようにするにはどうするべきか…
のように、途中にナンバーサインを含むキーワードがあるとそれ以降すべてハッシュタグだと認識されてしまうので、何らかの改良をしたい
案としては、ナンバーサインの前に改行またはスペースを必要にするというのはどうだろう
Unicode絵文字もtextに含めてパースした後、
textにtwemojiの正規表現を適用してUnicode絵文字をtextから分離する
https://misskey.io/notes/8jaxv3bqdj
Unicode絵文字→Twemojiとして表示する だからTwemojiで対応しているのを過不足なく指定する必要があってTwemojiと同じパターンを使ってるのだわ。他のパターンだと
U+FE0E
が付く付かないのあたりがうまく合致しなかった気がするのだわ。
パフォーマンス改善
リテラルを後から結合してテキストノードを生成する
オプションから与えた関数名リストに一致した場合のみ受理する
PEG.jsで宣言的に記述したパーサでは細かい制御ができないため、
再帰下降パーサを手書きして置き換えたい。
実験用ブランチ:
https://github.com/misskey-dev/mfm.js/tree/parser-alt-impl-1 (offsetを自動的に計算したいため実験中止)
https://github.com/misskey-dev/mfm.js/tree/parser-alt-impl-2 (offsetを自動計算できるようになった)
PEG.jsのプラグインを作成することで、パーサの動作を細かく制御できる可能性が出てきた。
まずはPEG.jsのプラグインでできることを調査する。
Related to #57
peg.jsリポジトリ上のドキュメント:
https://github.com/pegjs/pegjs/blob/master/docs/guides/plugins.md
peg.jsのforkのPeggyにある説明:
https://github.com/peggyjs/peggy#plugins-api
実験用ブランチ:
https://github.com/misskey-dev/mfm.js/tree/expr-pegjs-plugin-1
MFMとしての仕様を検討します。
現状のMFMパーサーは実装に依存しています。
書き直しで全ての挙動を再現することになり、その挙動に何らかの意図があるのかどうかが現状では不明です。
仕様を決定することでパーサーの挙動が最適化・簡素化されることを狙っています。
このほうが良いなどあれば、ご意見をください。
これに沿って実装を進めていきます。
"
"
で囲まれた文字列 : 普通の文字列を表します。
(
or
)
: 選択を表しています。
+
: 1回以上の繰り返しを表しています。
?
: オプションを表しています。あってもなくても大丈夫な部分です。
上記以外 : 項目毎にそれぞれ説明しています。
また、一部PEGの用語が含まれています。
タイトル行。現在位置から行末までマッチする。
beginline "【" inlines "】" endline
beginline:
行頭にマッチ(消費しない)
inlines:
改行文字を除く任意の文字にマッチ(複数)
マッチした文字列をインライン構文として処理(複数)
endline:
改行文字またはEOFにマッチ
タイトル行。現在位置から行末までマッチする。
beginline "[" inlines "]" endline
beginline:
行頭にマッチ(消費しない)
inlines:
改行文字を除く任意の文字にマッチ(複数)
マッチした文字列をインライン構文として処理(複数)
endline:
改行文字またはEOFにマッチ
引用ブロック。現在位置から行末までマッチする。
複数行で連結して1つの引用ブロックを形成する。
例外として、head">"に続いて"<"が出てきた場合や、head">"に続いて"<"が出てきた場合にはマッチしない。
beginline head content endline
beginline:
行頭にマッチ(消費しない)
head:
">" or ">"
content:
改行文字を除く任意の文字にマッチ(複数)
1文字目がスペースなら、2文字目から取り込む
取り込まれた文字列をブロック構文orインライン構文(複数)として処理
endline:
改行文字またはEOFにマッチ
検索ボックス。現在位置から行末までマッチする。
beginline query " " ("検索" or "search") endline
beginline:
行頭にマッチ(消費しない)
query:
改行文字を除く任意の文字にマッチ(複数)
マッチした文字列をそのまま保持
endline:
改行文字またはEOFにマッチ
コードブロック。「```」で囲まれた複数の行をコードブロックとして扱う
beginline "```" endline
(line endline)+
"```" endline
beginline:
行頭にマッチ(消費しない)
line:
改行文字を除く任意の文字にマッチ(複数)
マッチした文字列をそのまま保持
endline:
改行文字またはEOFにマッチ
数式ブロック
"\[" content "\]"
content:
任意の文字にマッチ(複数)
マッチした文字列を数式として処理
**寄せブロック
"<center>" content "</center>"
content:
任意の文字にマッチ(複数)
マッチした文字列をインライン構文として処理(複数)
拡大して揺れる太文字
"***" inlines "***"
inlines:
改行文字を除く任意の文字にマッチ(複数)
マッチした文字列をインライン構文として処理(複数)
太文字
"**" inlines "**"
inlines:
改行文字を除く任意の文字にマッチ(複数)
マッチした文字列をインライン構文として処理(複数)
太文字
"__" inlines "__"
inlines:
アルファベット(大文字 小文字) 数字 スペース のいずれかの文字にマッチ (複数)
マッチした文字列をインライン構文として処理(複数)
小さな文字
"<small>" inlines "</small>"
inlines:
改行文字を除く任意の文字にマッチ(複数)
マッチした文字列をインライン構文として処理(複数)
イタリック文字
"<i>" inlines "</i>"
inlines:
改行文字を除く任意の文字にマッチ(複数)
マッチした文字列をインライン構文として処理(複数)
"*" inlines "*"
inlines:
アルファベット(大文字 小文字) 数字 スペース のいずれかの文字にマッチ (複数)
マッチした文字列をインライン構文として処理(複数)
"_" inlines "_"
inlines:
アルファベット(大文字 小文字) 数字 スペース のいずれかの文字にマッチ (複数)
マッチした文字列をインライン構文として処理(複数)
打ち消し線の付いた文字
"~~" inlines "~~"
inlines:
改行文字を除く任意の文字にマッチ(複数)
マッチした文字列をインライン構文として処理(複数)
動く文字
"<motion>" inlines "</motion>"
inlines:
改行文字を除く任意の文字にマッチ(複数)
マッチした文字列をインライン構文として処理(複数)
"(((" inlines ")))"
inlines:
改行文字を除く任意の文字にマッチ(複数)
マッチした文字列をインライン構文として処理(複数)
"<spin" (" " attr)? ">" inlines "</spin>"
attr:
改行文字を除く任意の文字にマッチ(複数)
inlines:
改行文字を除く任意の文字にマッチ(複数)
マッチした文字列をインライン構文として処理(複数)
跳ねる文字
"<jump>" inlines "</jump>"
inlines:
改行文字を除く任意の文字にマッチ(複数)
マッチした文字列をインライン構文として処理(複数)
反転する
"<flip>" inlines "</flip>"
inlines:
改行文字を除く任意の文字にマッチ(複数)
マッチした文字列をインライン構文として処理(複数)
コード
"`" chars "`"
chars:
改行文字を除く任意の文字にマッチ(複数)
マッチした文字列をコードの内容として処理
数式
"\(" chars "\)"
chars:
改行文字を除く任意の文字にマッチ(複数)
マッチした文字列を数式の内容として処理
ハッシュタグ。
ネストされたかっこの開始と終了が対になっている場合のみハッシュタグの内容に含める処理を持つ。
"#" content
content:
基本的には以下の正規表現にマッチする文字を内容として取り込みます。
正規表現: [^\s!"#$%&'*+,\-./:;<=>?@\\^_`{|}~【】<>]
その中で、ネストされたかっこ()[]「」
の開始と終了が対になっている場合、その部分は内容に含めます。
それ以外の場合は、改行文字を除くかっこ()[]「」
に一致しない文字を内容に含めます。
プレーンな文字。他のルールに一致しなかった場合にフォールバックしてマッチする。
chars
chars:
改行文字を除く任意の文字にマッチ(複数)
本家のMFM実装で]は行末になくても良いことになってるけど、ブロックとしては違和感があるので変更したい
(インラインのように書けてしまうため)
punycodeはいずれ廃止されるため。
カスタム絵文字とUnicode絵文字をより扱いやすくする
引用構文を使う時、
> foo
bar
のように、直後に空行を設けるユーザーが多いが、そうすると視覚上無駄なスペースが生まれるため、パース結果では直後の空行は無いものとして扱うようにしたい。
ブロックの枠だけ処理して中身をテキストで返すパーサーを用意する
インラインのみの部分は一度で処理する
パーサーのアクション内でパーサーを呼び出すことができたので不要になりました
「`」のエスケープができたほうが良い気がする
Name | Progress | Comment |
---|---|---|
title | Done | |
quote | Done | |
search | Done | |
blockCode | Done | |
mathBlock | Done | |
center | Done |
Name | Progress | Comment |
---|---|---|
big | Done | |
bold | Done | |
small | Done | |
italic | Done | |
strike | Done | |
motion | Done | |
spin | Done | |
jump | Done | |
flip | Done | |
inlineCode | Done | |
mathInline | Done | |
mention | Not yet | |
hashtag | Done | |
url | Not yet | |
link | Not yet | |
emoji | Done | |
text | Done |
高負荷にならないように
#43 と同じ
例えば[email protected]
のような文字列もメンションと判定されてしまう
何もわからん
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.