2022/12/17 いったん最低限はできたのでおしまい、かな?
network層対応
✅links
✅links系のレンダリング
✅ghostpageのレンダリング
2hoplinks
pageの並びをsortする機構(page.count_of_char, page.count_of_clineとかも要りそう)
✅ネットワーク層をつくる(pageインスタンスレベル)
---
Network
Page
Line:
---
グラフをどうつくるか
scbjson2ghpagesのときは?
https://github.com/stakiran/scbjson2ghpages/blob/master/scbjson2ghpages.py#L135
https://github.com/stakiran/scbjson2ghpages/blob/master/scbjson2ghpages.py#L364
page
linkto_instancesとlinkfrom_instancesを持っている
linktoはupdate_linkto()で更新
linkfromはappend_to_linkfrom()で更新
いずれにせよ Page のインスタンスへの参照を持っている(ページ名ではなく)
2hopは、表示時にその場で計算している(pageには持たせない)
テストは?
手作業で(つくった)json を実際にスクボに食わせることで見ている
今回はテストコードにしてえなー🐰
今回は?
また一から書いてみるか
今度はわかりやすく、賢く、計算量も抑えて
---
linktoは辿るだけ
linkfromは二重ネスト不可避か
2hop
1 networkが持つすべてのpage(が持つすべてのlink)を辿って、すべてのpagenameをつくる
ゴーストリンク分も手に入れることができる
json
[
"pagename1",
"pagename2",
……
]
2 networkが持つすべてのpage(のインスタンス)を辿って、pagenameをキー、pageインスタンスを値とした辞書をつくる
json
{
"pagename1": page,
"pagename2": page,
...
}
このとき、ゴーストページ分のインスタンスもつくる
Page.is_ghost() もサポートするか
3 1+2より、各pageインスタンスに、linkto先のpageインスタンスをぶら下げる
page.linkto_pages
4 3より、各pageインスタンスに、linkfrom先のpageインスタンスをぶら下げる
py
# A-->B1
# |
# +-->B2
for A in all_pages:
for B in A.linkto_pages:
B.append_to_linkfrom(A)
5 3+4より、linksをつくる
links は linkto + linkfrom
ただ並び順が使いづらそうなので工夫が必要か……🐰
sta-scbではタイムスタンプがないので、別の指標を使う必要
page.指標プロパティ みたいなのをつくって、sortさせればよい
line_count, cline_count, length_by_string, codeblock_count etc
あるいは 2022/12/13 みたいな文字列を手動で書いているので、それをタイムスタンプにする案もある
……が、これは user defined でしかないので、このスクリとして実装するべきではない🐰
最後に、2hopリンクだけども……
py
# +---C2
# |
# v
# A-->B<--C1
#
# Aから見て、C1やC2を導く
#
for A in all_pages: # L1
for B in A.linkto_pages: # L2
for C in B.linkfrom_pages: # L3
A.append_to_2hoplink(C)
が、
計算量がやばすぎる
Aが持つ2hoplink先ページ数の数も増えるおそれ(100どころじゃない
scbjson2ghpagesのときは L3 や L2 を指定数以下で打ち切るカット処理を入れることでしのいでいたけど……
pagerank的な……
Page.pagerank を事前に計算しておく必要がある
✅pageに関連リンクコンテンツを追加するには
制約
page自体はnodeしか持ってない
事前に「関連リンクを表現するlines」を追加すると、それもコンテンツ(network計算時に見るリンク)に含まれちゃう
page1.extend(page2)
これか?
pageインスタンスはlinepasserとpageparserでつくれる
関連リンクはこんな感じ.scb
links
[a] [b]
2hop links
[b] ← [x] [y]
以下はたぶん一緒にやる
関連リンク部分のテキストをつくる処理
pageインスタンスに、そいつのlinktoやlinkfromをぶら下げる処理
✅関連リンクのレンダリング
1 page.linksから以下のような文字列をつくる
関連リンクはこんな感じ.scb
links
[a] [b]
2hop links
[b] ← [x] [y]
2 1をpageインスタンスに変換する
3 元々のpageインスタンスたち(linksを計算したもの)を2でextendする
---
1-3は self._page_dict[pagename] for pagename in self._page_dict
的なループで一気にできる
ページ表示順の並び替えを行うタイミングもここか
つまりレンダリング自体はHTML Rendererからいじらない
レンダリングする対象となるデータとして、1の文字列を増やすというだけ
ただのurlがリンク化されていない
ああ、そうか、ブラケティングしてないからか🐰
真面目に実装するなら……
Line._parse()の最後、plainに対してさらにかけるって感じだなぁ……🐰
spaceでsplitして、その中にurlがあれば、それらはlinkにしちゃう
zzz at 2022/12/16
✅linkfromがなんか重複してるんだが
fromもtoも、どちらも単純なappendしかしてなくて重複チェックしてない
案1: Page._linkfrom_pages などを list ではなく dict で持つ
テストしやすいので orderable にしたいんだよね……🐰
案2: Network._create_relation_links() 内で list(set()) する
楽なのは2、計算量に優しいのは1
じゃあ消去法で案2だな
一応計算量調べる
de_renderは0.4sくらい
---
処理時間に差はない、誤差レベルだな
が、やっぱり(内部的に一度setにしてるので)順番狂ってるなー
google「python remove duplicates from list keep order」
意味わからんすぎる seen.add
って何w🐰
list(dict.fromkeys(items))
こっちだな🐰
py3.7からだけど、まあいいだろ
pageインスタンスのリストを並べ替えるマン
たぶん要る
scrapboxでもページ一覧のみならず、関連ページリストでも機能させている
抜粋1
ただ並び順が使いづらそうなので工夫が必要か……🐰
sta-scbではタイムスタンプがないので、別の指標を使う必要
page.指標プロパティ みたいなのをつくって、sortさせればよい
line_count, cline_count, length_by_string, codeblock_count etc
あるいは 2022/12/13 みたいな文字列を手動で書いているので、それをタイムスタンプにする案もある
……が、これは user defined でしかないので、このスクリとして実装するべきではない🐰
stylesheetとtemplateのサポート
templateは外部ファイル化する。{title}はそのまま書かせればいい
stylesheetは……いらないか。template側で指定すればいい
blankline
たぶん Line.is_blank() が良い
---
3 ひとまずデプロイするまで
Structure:
Page
Node
indent: number
content: Block | Line
interface Block
startline: string
endline: string
CodeBlock
TableBlock
BlankBlock
Line
raw: string
inline_elements: InlineElement
QuoteLine
BlankLine
interface InlineElement
Plain
text: string
Link
url: string
text: string
Image
url: string
text: string
Literal
text: string
---
つまり、
Pageはn-Nodeを持つ
Nodeの本体はBlockまたはLine
Lineはn-InlineElementを持つ
Command:
python dobu.py OPTIONS
options:
以下は入力ファイル。単一かディレクトリ丸ごと
-i (Input-path)
入力ファイルを指定
--from-cd
カレントディレクトリをインプットにする。./*.scb
と同義
以下はフォーマット指定。なければ --to-dobu がデフォ
--to-dobu
--to-html
以下は出力形式指定。なければstdout
--file
ファイルに出す
出力ファイル名は固定
以下はフォーマットごとのオプション。--FORMAT-OPTIONNAME
という形にする
--html-template (template-htmlfile-path)
{contents}
と書かれた部分に、変換されたHTMLが挿入される
🐰
出力ファイル名の指定はない。常に{basename}.{output-format-extension}
で固定する。
1 そんなところで迷いたくないので
2 リンク文字列の変換を行う以上、名前のフォーマットを固定しないといけないので
❌以下はボツ
-g input Glob pattern
自由に指定させるためにglobに投げる🐰
デフォは /**/*.scb
まずはminimumにつくりたいので後回しでよい
formats:
Human Readable(to string)、拡張子は .dobu
HTML
dobuフォーマット
mebj用のreadable format
人間も読めるし、テストコードなどで照合もしやすい
登場人物
1 scb
入力ファイルフォーマット。hidemaru scbフォーマット
2 mebject
scbから変換する中間オブジェクト
✅先にデプロイをサポートする
ok 2022/12/12 18:32:30
✅コマンドのサポート
✅htmlファイルに出力してみて、気になるところ
✅GTD
引用ブロックがマージンが深い
✅繰り返し行うという営為
行が長くて続いてると正直読みにくい
✅ドキュメントビルダー
途中でコードブロックが終端していない
<code>
みたいにhtml書いてる場合にエスケープが要る🐰🐰
これはhtml renderrerの話なので、そこでやる
trash
定義生成パターンが空ファイル
✅長い行が続くと読みにくい系
フォント
区切りの設定
とりあえず適当にcssでborder-leftを当てた
✅引用のマージンが深い
CSS適用されてないだけだった
✅引用の >
を消す
✅エスケープ対処
text, url, linesなど実際に差し込む部分に対してのみ行う必要がある
nodefactoryかなぁ🐰
いや、利用者の責務だから、利用者側で頑張るべき
生成元は必要最小限であるべきだろ
htmlrendererの各場所でもれなく行うしかないな……🐰
エスケープ関数でくくっちゃうか
espace()
✅パーサ一通り書いたけど次は?
CLIを経由した出力ワンパス
まだhtmlはない。内部的なdobu形式
to string。json?
to mebj ここの位置づけどうするんだ?🐰
to json
to html
---
mebjは内部的なオブジェクト。そもそも特定のフォーマットは持たない
が、それじゃテストができない
何らかのフォーマットを出す必要がある
✅Renderer方針
こうだな
1 /to html
2 /to json
2 /to plain text
2 /to markdown
3 /to json
1 不可逆でfor human rich。はmust。だがまずはmebjのテストしたいので後
2 不可逆でfor human plain。はmebjのテストのために何かしら要る。for humanならあえてjson選ぶ必要はないと思うが
3 可逆。今は必要性わからん。
2の不可逆 for human plainをどうするか……
node自体は一次元リストで繋いでるし、markdownでいいか?
1-section 1-nodeで並べればいいだけだし
section間は必ず空行を入れる。あるいは区切り線
lineは、sub sectionつくるか……
Rendererクラス
どこまでi/fにするか
1 renderメソッド程度
2 lineやcodeblockなど各データクラス単位
まあこっちだよなぁ
pageのparseは基底側で行ってしまいたい
✅複数ファイルのパースと保存
directory2filenames()
filename2page()
page2file()
寝かせる
ok
✅HTML Rendererに改行を消すオプションを入れる
Renderer に、parse_as_line の後の lines を oneline にするオプションを入れる
use_line_flattening
ok 2022/12/10 08:34:31
✅HTML Renderer
どういうhtml tagやcss使えばいいか何もわからんのだが
基本的にdiv行を列挙する
codeは<code>
で、ファイル名部分はspan、それ以外はdivで囲ってmargin-leftだけ入れて、あとはベタ書き
inline要素も全部div
classはline, icon, page-link
page-linkは<a>
インデントはdivにmargin-leftを入れる
cssはパクればいいか🐰
MITだし
いや、勉強にならんから、地道に書いて反映させていくことにしようぜ🐰
---
1 テキトーに書いた
で、ぱっと目についたのは
実行ごとにhtmlファイルを生成したい
cssクラス
page title入れる
codeblockにindentが及んでない
リンク先のファイル名変換
---
2 どこに何の属性をつければいいんだ?
scrapbox-reader読んでる🐰
div
class="line"
code
class="code-block"
a
class="page-link"
blockquote
class="quote"
話逸れるけど、div class="line"の中にblockquoteもcodeもある感じ🐰
ああ、そうか、html的にはどっちもブロックだから問題ないのか
class lineといいつつ、複数行が入ることがある(codeの場合)
lineというか、nodeってニュアンスだよな
話逸れるけど2、全部divで囲ってるのか
やっぱりdivがnodeの位置づけだな🐰
今はそうなってない
scrapbox-readerは、ブロックも見かけ上はlineごとに表現している
だから全部 div class = "line" なんだよな?🐰
結局、classはどこにつければいいんだ?
結局はcssでいじれれば何でもいいよなぁ
🐰的には
div側にはlineとかcodeblockとかつけたい
divの中、codeやaやblockquoteは?
たぶん何かしら要るはず。でないと細かい調整できないだろうし
🐰
divにはclass="node"
その中の各html要素はまあよしなに
ok。あとは試しながらでやるしかねえ 2022/12/09
---
3 cssどうする?
scrapbox-readerのコピペした
が、クラスを全部このとおりに合わせるのはよーわからんなぁ🐰
中身はこれを使いつつ、一つずつ足してみるか……
white-space:pre-wrap;
spanレベルで折り返されてlineが1行にならないので、normalにする
が、codeの中身も改行されてないので、別途調整が要るか
❌div内のspan間に空白ができる問題
結論
対処自体は、html上の改行を消す実装を入れる必要がある
---
1 inline-block; で配置する
2 html上の改行のせいで空白になっているのを除去する
物理的に消すか、<!-->みたいなコメントでpaddingするか
どっちも微妙……🐰
3 display: table; で、そもそもブロック要素をやめる
3のやり方だとダメだ🐰
やっぱり改行を消すしかねえのか……
---
white-space?
スクボは .line に white-space: pre-wrap; を当てているが、俺のだと普通に改行されてしまう……
css
.line{
white-space: pre-wrap;
word-wrap: break-word;
}
div > span ではなく span > span にすればいい?
スクボもそうなってるわ
が、真似してもダメだなぁ🐰
上記 white space とか入れても同様
---
いや、スクボもhtmlとしては改行無かったわ🐰🐰🐰
開発者ツール上で、inner htmlをコピーしてみると違いが出た
✅空白のplainが入る問題
机上で解消
テストコードでやっぱり一通りやっておくべき(to_Lr でサボってたやつだなw)
---
html
<div class="node">...
<span class="plain"></span>
<span class="link"><a href="https://scrapbox.io/sta/">前に スペース区切りで sta</a></span>
<span class="plain"></span>
</div>
literalでも入るので、あのアルゴリズムだな
そっか、headとtail、必ず入れるようになってる🐰
空のときは弾くべき
先テストコード書くか
done 2022/12/09 05:40:04
✅引用がないのでサポートしろ
実装
✅デバッグ用データ
テストデータ
all green
---
どこで解釈すればいいんだ?
Lineクラスに is_quote みたいな判定つける?
Lineを使う側で判断してもらう?
今
py
def proceeded_as_line(self, line):
lineobj = Line(line)
nodecontent = NodeContent()
nodecontent.set_line(lineobj)
return nodecontent
blockなどを先に処理して、どれにも当てはまらなかったら最後にlineとみなしてる感じ
あとQuoteLineクラスなんかとりあえずつくってる
ここで判定するのが楽だよなぁ🐰
先頭の>
があったら、QuoteLineをつくればいいだけ
うーん
Line.enable_quote() とかの方がよくないか?
呼び出し元で統一的に扱うためには、Lineのインスタンスを前提とするべき
引用かどうかは line.is_quote() とかで統一的に判定できる
インターフェースの場合だとそもそもサブ側のQuoteLineを扱えない(Lineインターフェースで扱えなくなる)
うん、これ🐰
寝かせるzzz
ok 2022/12/07
✅lineのパースどうやる?
1 正規表現
2 自製で頑張る
3 inlineelementの特定までは自製、その後は正規表現
---
1はどうやるんだっけ?
前から順番にヒットさせて、ヒットさせたところは除外して次からはじめて、みたいなこと、正規表現でできるっけ?
https://github.com/stakiran/scbjson2ghpages のときに色々正規表現頑張った気がするけど覚えてねえ🐰
たしか正規表現と「前から順番に処理する逐次」は相性悪かった覚えがある……🐰
入れ子とかないし、2で行っちゃった方が案外早いかもしんね🐰
と思ってたら3はどう?
literal は `
link と image は [
うん、これやな……🐰
---
たとえばリテラルはリテラル終点以外全部を含めるんだから、開始見つかった時点で終点までごっそり飛ばすしかないよね
リンクは?
[AAA]
[aaa[AAA]]
[aaa[aaa[AAA]]]
[aaa[aaa[AAA]aaaa[AAA]]aaa[AAA]
もっとも内側だけ採用される形になる
これはスタックだなぁ🐰
でもスタックだけだと「最も内側 "だけ" 採用」ってのができない
最内側の一つ外も解釈してしまう……
採用した分もスタックに詰めればいいのか🐰
[aaa[aaa[AAA]aaaa[AAA]]aaa[AAA]
1 2 3 4 5 67 8 9
1 [
2 [[
3 [[[
4 [[!
5 [[![
6 [[!!
7 [[!! !があるので、!の左側の[にリーチできない。リンクにはしない
8 [[!![
9 [[!!! !の右側に積まれてる [ を解釈するので、リンクにできる
リンク用スタック
[
とその位置
!
うん、見えた
1 まずはリテラルをパースする。開始見つかった時点で終了も探しに行く。
2 次にリンクをパースする。リンク用スタックを使って、!の内側に侵食しない形で確定していく。
あとは組み立て
1の段階で xxx●xx●●xx●xx
みたいに(●がリテラル部分)に置換できる
もっと言えばこれは inlineelement, literal, inlineelement, literal, literal, inlineelement, literal, inlineelement
2は、inlineelement各々について行えば良い
リンクはリテラルをまたげないので、これで良いはず……🐰
合ってる、よね?🐰🐰
Lineとして再帰的にパースすればよい!🐰
とすると、Line.extend() も要る
---
どう実装すればいいかが浮かばん……
1 line全体から成るplain element
これをnのelementに分割したい
2 まずはliteralをパース
これで plain, literal, plain, literal, literal, plain, literal, plain になる
plain部分はlinkなど他の文法が残ってる可能性があるから再パースが要る
3 linkとして再パース
これでたとえば plain, literal, (link, plain), literal, literal, (plain, link, plain), literal, (link, link, plain) になる
今のところないけど、link以外にも文法があれば、さらにそれも再パースが要る🐰
……
で、すべてのパースを終えたら、
4 plainはplainのまま確定させる
これで plain, literal, link, plain, literal, literal, plain, link, plain, literal, link, link, plain
plainだとややこしいから、未確定 undefined にする
実装中... 2022/11/26 08:25:35
❌literal parseのテストを書きたい to_Lr
literalのparseが終わった時点の inlineelements が見れればいい
どうやる?🐰
self._inline_elements_at_literal_parsed みたいなのを内部的にもたせてしまう
で、テストから直アクセス。行儀悪いけど、まあええやろ
パターン全部網羅すべき
undef
literal
undef literal
literal undef
---
undef literal undef literal
literal undef literal undef
---
literal literal
undef literal literal
literal literal undef
まあこれくらいでええか
複数回出現も捉えたい
aaa
bbb
みたいな連続パターンも捉えたい
07:13:54 いったんサボる……
✅line中のブラケティングを取得する方法
それも plain 部分も順序保存した上で頑張る必要がある……
正規表現だと厳しい気がする……
aaa
aaa[bbb[ccc]bbb]
^^^^^^^ ^^^ ^^^^
1 2 3
plain1, link2, plain3
aaa[bbb[ccc]bbb[ccc[ddd]bbb]]
^^^^^^^ ^^^ ^^^^^^^ ^^^ ^^^^^
1 2 3 4 5
plain1, link2, plain3, link4, plain5
aaa[bbb]aaaaa[bbb]aaaa[bbb][bbb]aaaaa
^^^ ^^^ ^^^^^ ^^^ ^^^^ ^^^ ^^^ ^^^^^
1 2 3 4 5 6 7 8
plain1, link2, plain3, link4, plain5, link6, link7, plain8
❌アルゴリズムとしては、
右端から見に行って、
1 [
があれば
2 対向の]
までをリンクにする
この間に [
があった場合は、そこで探索を打ち止め
いや、左端からでよくね?
左端から見に行って、
1 [
があれば
2 対向の]
までをリンクにする
ただしその間に[
があった場合は、その探索は打ち止め
かつ、見つけたそこから再開する
✅同上、inline elements リストを維持しながら展開していく方法
literalと同じく head body tail方式でいける
✅あとは、linkの細かい違いをどう表現するか🐰
[ページ内リンク]
[uriつきリンク https://www.google.co.jp]
[https://www.google.co.jp uriつきリンク]
[https://gyazo.com/979e6478f2df64b08ad95863e751d405]
画像
[https://gyazo.com/979e6478f2df64b08ad95863e751d405/raw]
画像
今は raw, text, uriしか持たせてない
足りるか🐰
画像の判定はどうやればいいんだ?
通信して見に行かないとダメだよな?(ScrapboxはGyazoと開発元同じだから密に見に行けてるだろうけど)
そもそもstascbでは画像使ってない
が、https:// があると url とみなすのでリンク先を開く挙動になる
つまり
1 [ページ内リンク]
2 [uriつきリンク https://www.google.co.jp]
3 [https://www.google.co.jp uriつきリンク]
4 [https://gyazo.com/979e6478f2df64b08ad95863e751d405]
URI
この4パターンを想定すればいい🐰🐰
a https:// の有無を調べる
b split(' ')で分割されるかを調べる
if a and b then 2か3
if a and not b then 4
else 1
この判定は Line クラスで行う
で、Link なり Image なりをつくればいい(今のところ Image は無いので Link 一択かな)
✅codeblockやlineクラスの処理内容に一貫性がない……
今
codeblockは、外からlinesを与えるだけ
lineは、中でパースしようとしている
外でやって与えるのか、中でパースするのか、統一したいよね🐰
misc
1 少なくともcodeblockの中でパースするには、lpを渡してやる必要がある
2 中でパースした方が綺麗
1のせいでcodeblockは外からlinesを与える形にしているなう🐰
が、これも間に「lpを解釈してコードブロック分のlinesをgetして、それをcodeblockにわたす」役割の奴を入れればいいよね?🐰 to_5j
改めて、こいつらのクラスには何をさせたいの?
文字列データを与えてパースしてほしい
文字列の範囲は呼び出し元で保証する
使いやすいよう情報を整えて、使いやすいi/f経由で使えるようにしてほしい
うん、だよな🐰
とすると、 to_5j は正しい🐰
呼び出し元で保証すればいいんだよ
ok
いや、現時点で1も別に普通に呼び出し元でパースしてたわ🐰
✅XXXX is not defined Pylance(reportUndefinedVariable)
XXXXが見えてない
このエラー出てるよりも前でXXXXを定義する必要がある
PylanceはVSCodeとかで使われてる言語サーバーのこと
というか、reportUndefinedVariableという言い回しもpylanceのものっぽいね🐰
❌codeblock内部のインデントってどう取ったらいい?
scb記法的にはブッレブレだよなぁ……🐰
2 スペースで下げなくてもいける(そもそも終端文字あるしね)
aaa
aaa
3 終端文字はインデント深さ不揃いでも良かったりするし
aaa
aaa
上記は全部コードブロックなのですが……
インデントどう取ればいいんだ?w
1 は codestart のインデント数だけ取り除けばいい
2 は何も取り除かなくていい
1と2はどうやって区別すればいい?codestartの次の行か?
ムズい……
というか記法レベルで曖昧だから、たぶんきれいな確定は無理だ……🐰
諦めて「そのまま保持」でいいか?つまり常に2として解釈する
うん、いったんこれにする 2022/11/20
まずは形にしたいし🐰
✅blockやlineをつくるところをどう書くかに悩んでる
1: parserが、Nodeに、必要に応じてlineを与えることで、Node側で確定させる
2: Nodeにlinepasserを渡して、Node側で確定させる
3: linepasserを使う確定者を用意して、確定後、Nodeをつくる
NodeFactory
already_empty()
determin()
Node一つ分を確定させる
内部的に lp.next を n 回行う
1つのline or blockをつくり、さらにnodeもつくる
このnodeをreturnする
✅HTMLのカスタマイズ
headerとfooter、あるいはテンプレートつくって{contents}
と書いたところに埋め込む、みたいなやつ
cssでいじれるようにするためクラス名はちゃんとつけるべき
ここは試行錯誤しながら、過不足なく揃えていくしかないなー🐰
どういうcssクラスをつけるかは外から与えられるようにしたい
scrapbox-readerのやつを使いたい、とかしたいし
どうやればいいんだ……?
いったん後回し……
とりあえず自分用なのでハードコードすればいいさ……🐰
✅中間オブジェクト
scb to XXX
XXX to HTML
XXX to Markdown
このXXXにあたるデータ構造のこと
最初は「中間言語」と呼んでいたが、文法仕様があるわけではなく単にオブジェクトとして保持して扱うだけなので、たぶん「オブジェクト」が正しい🐰
長いので mediator object = mebject とでも呼ぶことにする🐰
通称mebj
✅コマンド名
良いの思いつかない……
打ちやすいのがいい
今は雑にdocとかにしておく🐰
いや、わからんのでなし
document builder で dobu
ドブw
---
2 structure決めるまで
✅クラインを扱うためにはどうする?
page側でclineの操作をサポートしたい?
空行ブロックをつくれば事実上サポートできることになる?
個人的にこれは欲しい🐰
空行は解釈に曖昧性が出やすいので、このパーサ側で全部要素化してしまいたい
が、利用者側でクライン使うってのは、直近はなさそう
クライン数でランキングつくるくらいか?インデックスページとして
後からも足せるし、クラインは要らないな🐰
✅あとは、空行の取り扱い
空行ブロック案
こっちは強引って気がする
ブロックはインデント下げてその中を隠蔽するってイメージなので……
空ライン案
いったんこっちでいいか🐰
✅ただのリストもブロックにするべきか?
Blockのメンバ設計とも絡む
まったくピンと来ねえんだけど……🐰
が、ブロックを下記案1 隠蔽式にしたので、ただのリストは無しでいいのでは?
それにリストブロックサポートすると、compositパターンみたいなことになる
ちょっと煩雑かなぁ
でもリストブロックにして構造化しておけば、利用者側で扱うのが楽だ
行入れ替えとか
いや、だから、行入れ替えは今回しねえだろ🐰
エディタつくるわけじゃないし
ならリストブロックはなしでいい🐰
✅ブロック内の要素はどう表現する?クラス?
ブロックごとに系統が違うから実装が独自になるよねぇ……
例:codeだとただの行だろうし、tableだと頑張ってパースしてテーブル形式にしたい
案1 XXXBlockでは内部的にパース処理を書いて、I/Fとしては便利メソッドやプロパティを見せるだけ
案2 案1+ブロックの下層にあたる構造もちゃんと定義する
無理じゃね?
……案1でいいか。他に浮かばん。案2は綺麗さ求めすぎな気もするしね →手段の目的化
✅引用はどう持たせるべきか。frontline的なやつとか、Line.is_quoteとか
is_quoteの場合、判定処理や本文抜き出しをLineが担うことになる。他にもfront系が来たらLineの責務が増える。
frontlineelementは違う。element単体で完結するべき(本文も含んでいるべき)だが、quoteは>
しか含んでない。違う。
QuoteLine……くらいしかねえよなぁ
✅ネストはどこで持たせる?
後述のとおり、Nodeで持たせる
利用者は、Nodeが持つインデント情報を「必要なら」使う
Scrapboxレベルのネスト表現力(ネスト中にコードブロックとか)を持ってるとは限らないので、強制はしない🐰
ブロックとは
コンテンツも機能も一手に引き受けた単位
Pageからは「ほげほげブロック」ということしか見えない
lineがどうなってるとかは見えない🐰🐰
逆を言えば、ほげほげブロックは、自分が持つlinesをどう扱いどう見せるかを自身が責任負う
---
1 下調べと検討初手
bnf2
<page> ::= <nodes>
<nodes> ::= <node> <nodes> | ""
<node> ::= <block> | <line>
<block> ::= <codeblock> | <tableblock> | <quoteblock>
<line> ::= <indent> <inline-elements>
<inline-elements> ::= <inline-element> <inline-elements> | ""
<inline-element> ::= <plain> | <link> | <image> | <literal>
<indent> ::= " " <indent> | ""
bnf1
<page> ::= <nodes>
<nodes> ::= <node> <nodes> | ""
<node> ::= <block> | <line>
<block> ::= <codeblock> | <tableblock> | <quoteblock>
<line> ::= <indent> <inline-elements>
<inline-elements> ::= <inline-element> <inline-elements> | ""
<inline-element> ::= <plain> | <link> | <image> | <literal>
<indent> ::= " " <indent> | ""
1行内にはnのインライン要素があるという感じ
class Line
インライン要素は色々あるので、基底を定義してこれを実装させる
interface InlineElement
class Plain
class Link
class Literal
class Image
class CodeStart
class InCode
class CodeEnd
データ構造
ルート
class Page
行はノードとして扱う感じ
class Node
indent: number
content: Line
1行内にはnのインライン要素があるという感じ
class Line
インライン要素は色々あるので、基底を定義してこれを実装させる
interface InlineElement
class Plain
class Link
class Literal
class Image
class CodeStart
class InCode
class CodeEnd
❌引用はどう表現する?
ブロックでいい🐰
最低でも行まるごとになる。これは1行からなるブロックとみなせる
そもそも引用記法は本来はブロック的だよね
ただ先頭に>
付ける書き方が楽だからそうなってる(インラインっぽく感じる)だけで
いや、いいの、か……?
でもinlineにする場合、indentみたいに「先頭につける」性質があるから、また特殊なんだよなぁ
愚直にやるならinline, frontline, backlineと3パターンサポートする羽目になる🐰
そして構文的にはこれが正しい
実際、scrapboxでも引用表記の中にリンクとか書ける。これはinlineとfrontlineが同列だからだ🐰🐰
クラインは?
scbを扱う上で俺がかなり意識している概念
サポートしたい
Blankみたいな概念がある?
Nodeと同列の概念で、n行の空行を表現する
Pageとは、NodeとBlankからなるリストと呼ぶことができる
……いや、Nodeの中にBlankを入れればいいのか🐰
そしてBlankも二つあるけど
indent=0のblank
indent>0のblank
つまりどこで(どの階層で)区切っているかが違う🐰
ネストはどう表現する?
Nodeにindent: numberを持たせる、ではダメなんだっけ?
問題ないだろ🐰
懸念してた「並び替え操作しやすくする」は、リストの操作の話なのでここじゃねえし
ネストの付け方に制約はないので、ポリシー的なものを搭載する必要もない
別に
こんな風に
1段ずつネストせず一気に
飛ばしたっていいんだし
本当に?🐰
HTML側はそれじゃ表現できなくね?
1段ずつの構造であるべきじゃね?
じゃあ1段ずつが守られてないscbがあったとして、どうするの?
エラーにする?うそぅ?
どう見せるかは中間言語使う側が考えることだろ🐰
HTMLを使う場合、HTMLでは使えないので「preで見せるか」とかやればいい
あるいはscrapbox-reader的に行指向にしてもいいし
---
Nodeとして「List」をサポートする案は?
より木構造的になる
いや、indentわからん(いちいち木を走査して深さを計算する必要がある)くてだるいよね
メリットは、
並び替えするときにList Nodeまるごと移せばいいだけになるので楽
が、やっぱりNodeではない気がするなぁ
BlockもListの性質を持つときがある。並列ではない
いや、やっぱり扱いたいですよねぇ
一貫したデータ構造にしたい🐰
before
<node> ::= <block> | <line>
after
?
block, list, line
listはlistblock?
要らんだろ。並び替えはしないよね🐰🐰
今ここで検討してるのはただの変換だ。
---
いや、行指向で行こう
HTMLなど変換先側の言語がネストに対応しているとは限らない
いや、それをいうならそもそもリストの中にブロック持たせる、自体がHTMLは対応していないが……w
ので、見かけ上はフラットな構造で持たせておいて、インデントはプロパティとして持っておいて各自解釈してよしなにしてもらう
つまりインデントという「構造を示した値」があるので、それで表示を微調整してくれ、と(scrapbox-readerのやり方)
page
node(indent=0)
node(indent=0)
node(indent=1)
...
パース
一行目から静的に行単位でパースする、でいいんだっけ?🐰
良いでしょ
ただ、ブロックの解釈時は、始点見つけたら終点まで一気に解釈して、続きはその後ろから、って感じになるはず🐰
1 まずはnodeレベルでリストつくる
2 その後、node各々をinlineレベルでリスト化する
✅目的は? ほか
1 この読み物をHTMLで公開するために、変換をつくること
2 scb記法一般について、1ができるようにしたい
3 クラインなどよく使う概念も取り込んで、プログラムで扱いやすくしたい(漠然!)
---
結論: 1でも結局中間言語作るレベルじゃないとたぶん通用しないので、素直に2のつもりでやればよろし🐰
---
fimp🐰
できれば2が欲しい。この読み物以外にもscbは結構使っているし、手元で書いたのを公開したいときとかにこれがあると仕事でも便利だろうから
が、直近は1でもいい。だらだらしても仕方ないし、2はscb記法ちゃんと考えて変換もちゃんとつくるってことだから一朝一夕ではない。1でいいのでは?
仮に1に特化して実装するとしたら?🐰
scb to htmlを直に書くことになる
厳密に設計せずとも、適当にパースすればよい
が、行指向的に前から一行ずつ処理する、だと地獄を見たので、nodeをつくる(scrapbox-parser)系のアルゴリズムにはしたい
他に良いアルゴリズムはあるか?思いつかん……
あとでデバッグが大変なので、テストコードは先にガッツリやってしまいたい
いや、結局scb to 中間言語要るなぁ🐰🐰
ただのそのクオリティをどこまで突き詰めるかの問題
うん、scb to 中間言語と中間言語 to HTMLにしよう🐰
✅ブロックはどう表現する?
中間言語としては、いちいちstart/in/endなんて意識したくない
nodeレベルにすることにした
✅react使うかで悩んでいる構図
inbox
React側 まずは二行の箇条書きを表示する程度でつくればいいのでは
スクボきほうのデータを直にフロントにもたせてしまうか、中間言語まではパイソンにするか。後者かな。他にもパイソンは使いそうだし。中間言語to、のひとつをリアクトでつくる。インポートするファイル(中間言語データが書かれている)を直につくりたさ……が、なんか微妙。前者でつくりきる方がいいか?もうひとつのコンポーネントにしちゃうとか
もうフロントにデータ載せて動的に描画するのもアリか?
いわゆるCSR、client side rendering になるのか?
Reactって使える?
使える
まさにrender()の中でhtml文字列つくってるじゃんね🐰
---
まとめると、
scb to 中間言語はn言語でつくれる余地がある
js
py etc
中間言語 to HTMLは以下のアプローチが考えられる
1 何らかの言語でコマンドをつくる
2 何らかの言語が読める形式に変換し、それを使ってその言語でツールをつくる
で、react使う案は2になる
scb to 中間言語して、
中間言語データを、jsで読める形式にしてreactのpjに入れる
あとはreactでよしなに、みたいな🐰
どうしたい?🐰
reactは何もわからないし、学びながらはきついので、いったんなし
どちらかと言えば今後のためにもscb to HTMLが欲しい
コマンドがあるといい
慣れたpythonで良い
✅scb to 中間言語変換をつくる
まずはこれをつくる
で、その後、中間言語 to HTML とか、to Markdownとかすればいい
中間言語の表現力は、まあHTML相当で良かろう🐰
✅ghpaages or vercel
docs/ 配下の github pages でいい
github actions
面倒くさそうならvercelに逃げる🐰
✅阿部寛レベルの軽いサイトにしたいかも
cssでbulletを消す、マージンとかも調整する
scrapbox-readerどうしてるっけ?
<div style="margin-left: XXXXem;" class="line">XXXX</div>
でしたわ
良いアイデア🐰
これなら箇条書き内にコード記法、も表現できる(コード記法みたいな装飾で見せればいいだけ)
そもそも箇条書き(ul and li)使わなくなるわけだし
---