図示 (hikari diagram) 仕様
本書は hikari diagram の仕様を定める。起動ディスパッチ全体は hikari-command.md、言語の意味論は language-spec.md、図の中に出るソースの綴りは format.md を参照。
.hika を、ブラウザーで開ける 1 枚の図に変換する。読み手は自分の書いたコードを読み直す開発者であり、図が答えるのは「この値はどういう形か」と「この名前はどこを指すか」の 2 つである。
何を図にして、何を図にしないか
コードはコードのまま出す。 :=・.・!・(・)・演算子・文字列・数値は、書かれたとおりの字面で図に現れる。これらを図形や矢印へ置き換えない。
図が足すのは次の 3 つに限る。
- 入れ子を空間にする — リテラルを箱にすると、何が何の中にあるかをインデントを数えずに読める
- 書かれた区切りを線にする —
|とmatchのアームの=>が線になり、その左右が面積として分かれる。片方が空なら空欄として見える。分岐 (matchのアーム・ifの then/else) は同じ箱の中で上下に積み、境目にも線を引く - 名前を色で分ける — 宣言と参照が結び付き、外から捕獲している名前がその場で分かる
線にした記号は字面としては残さない。 | も => も、線が既にその位置を示している — 同じことを 2 度描くことになる。線にするのはこの 2 つだけで、他の記号は字面のままである。
記法を作らないことが規約である。 図のための語彙を増やすと、読み手は自分のコードを読むために別の記法をもう 1 つ覚えることになる。:= や ! は既に短く、書き手が学習済みの綴りであり、図形に置き換えても得るものが無い。新しいマークを足す提案は、「そのマークが無いと読み手の判断が変わるか」で判断する。
構文
hikari diagram [--svg] [--no-comments] [--imports] <path>
引数の並びは フラグ → パス の順で書く (フラグは位置を問わず読む)。path は .hika ファイル 1 つで、--imports を渡したときはそこが 入口 になる(ディレクトリは受け付けない。「将来枠」)。
| フラグ | 意味 |
|---|---|
| (なし) | 自己完結した HTML 文書を stdout に書く |
--svg |
<svg> 要素だけを stdout に書く |
--no-comments |
コメントを図に出さない(「コメントの非表示」) |
--imports |
入口から import を辿り、届いた .hika を 1 枚に載せる(「ファイルを跨ぐ」) |
ファイル先頭の扱いは hikari check と同じである(先頭に宣言行は要らない。hikari-command.md §5.1)。
出力
外部資源を一切参照しない。 CSS も配色もフォント指定も出力の中に持ち、ネットワークもローカルファイルも読まない。図をそのまま添付・共有できることを要件とする。
- HTML 出力 —
<!doctype html>文書。<style>・<svg>・ホバー用の短い<script>を含む。配色はprefers-color-schemeで明暗の両方を持ち、載せる側が明示もできる(下記)。 --svg出力 —<svg>要素 1 つ。<style>は内側に持つが<script>は持たないため、ホバーの連動は働かない。型注釈は<title>としてブラウザーの既定のツールチップに出る。
図の本体は SVG である。HTML はそれを包むだけで、レイアウトをブラウザーの流し込みに委ねる部分を持たない。
包む部品のエクスポート
包みは 2 つの部品に分けて取り出せる。 図を自前のページへ載せる側は、<svg> 要素と、それを活かす CSS・<script> を別々に受け取って自分のページへ組み込める。
- CSS — 型注釈の吹き出しの見た目と、
<svg>を縮めないための規則。ページの地の配色と余白は含まない — あれはページの枠であって図の部品ではなく、載せる側は自分の地を既に持っている。 <script>— ホバーの連動・吹き出し・折りたたみ・宣言へのジャンプを張る短い綴り。ルートの要素を受け取って何度でも張り直せる形で渡す。図を組み直す載せ方(エディターの内容から作り直す等)では、ページの読み込み時に 1 度走るだけでは足りないからである。同じルートへ二度張っても吹き出しが二重に出ることはない。
HTML 出力はこの 2 つと <svg> にページの枠を足したものであり、部品とページで図の綴りが 2 通りになることはない。
明暗の明示
ルートの要素に data-hikari-theme="dark" / "light" を立てると、prefers-color-scheme に優先してその明暗で描く。prefers-color-scheme が見るのは閲覧環境の設定であって、図を埋め込んだ側の見た目ではない — エディターのパネルに載せると、暗いテーマの中で図だけが明るいまま出る。載せる側が自分の明暗へ揃えられるようにするための口である。
属性 1 つで、配色の値は出力の中に閉じたままである。 載せる側が色を持つわけではないので、「外部資源を一切参照しない」も「配色を出力の中に持つ」も崩れない。属性を立てなければ従来どおり prefers-color-scheme に従う。
出力の見本を diagram-sample.html に 1 枚置いてある(種は examples/enum_demo.hika)。以下の綴りは、そのまま見本の中で見比べられる。
図の中のソースの綴り
図に出るソースの字面は hikari format の正規形と一致させる。フォーマッターの出力を図の入力とすることで、hikari format を掛けたソースと図の見た目が食い違わない。行の折り返しは行わず、長い行は図の幅として伸びる(読み手は横スクロールする)。
字面はフォーマッターが決め、箱の置き場所は図が決める。 インデントは既に図の側の裁量である(下記「高い箱は頭の下へ落とす」・箱の中のインデントは箱そのものが表すので字面としては持たない)。図が動かすのは空白だけで、書かれた綴りは 1 バイトも足さず落とさない。
コメントはフォーマッターと同じ位置に、地の色で出す。
高い箱は頭の下へ落とす
2 行以上を占める箱は、頭の字面の次の行へインデントして置く。 同じ行に置くと、箱が高いほど頭の字面が箱の縦の真ん中へ浮き、f := や match s がどの箱の頭なのかを読み手が目で追うことになる。
- 判断の元は箱の行数だけである。 名前の一覧(
if/match/ …)も、幅のしきい値も持たない。1 行に収まる箱は頭の字面が浮きようがないので落とさず、2 行以上の箱は必ず浮く。同じ形の箱が位置によって 2 通りに描かれることも、閲覧環境で判断が変わることも無い - 区画を 2 つ以上持つ箱(
matchのアーム表・まとめたifの 2 分岐)は、それだけで 2 行以上である - 箱の後に残る字面(後置の
!・閉じ括弧)は、さらに次の行へ頭に揃えて置く。字面は落とさない — 落とすのは置き場所の話であって、書かれたものを消す話ではない - ファイルの箱も同じ規則に従う。箱の直前に置くパスの綴りが 1 行目に立ち、箱はその下へ来る
コメントの非表示
--no-comments を渡すと、コメントを図に出さない。既定は出す — コメントはソースの字面であり、図はソースの表示だからである。
落としたいのは、図を構造として読みたいときである。図の箱は中身の幅で決まるので、長い注記が 1 つあるとその箱が注記の幅まで広がり、入れ子と区切りという図の本題が押しやられる。
- 落ちるのはコメントだけである。コードの字面は 1 バイトも変わらない
- コメントだけの行は行ごと落ちる。綴りだけ落として行を残すと、書き手が空けたのでない空行が図に出る。行末に付いたコメントは手前の空白ごと落とし、改行は残す — その行にはコードがある
- 文字列・三連引用符の中の
#は落ちない。コメントの見分けはフォーマッターの走査に訊く(compiler/format)ので、読み方が図の側で割れることはない - コメントだけで立っていた区画(「アームの外に書かれたコメント」)は、区画ごと消える。残すと中身の無い区画が立ち、横線だけが増える
VS Code 拡張はパネルのツールバーで、Playground は図のペインのツールバーで、どちらも図を出したまま同じ指定を切り替える。
図の綴り
読み手が覚える規則は次の 5 つで全部である。
箱 — ソースに書かれた { } / [ ]
リテラル 1 つが箱 1 つになる。入れ子はそのまま箱の入れ子になる。{ } は角丸、[ ] は角を立てて描き分ける。
ファイルも箱である。 ファイルレベルは暗黙のリテラルであり (language-spec.md §13.1)、{ } を字面に書かないだけで、slot-list 領域と body 領域を持つ closed なリテラルそのものである。したがって図はファイルを角丸の箱 1 つとして描き、ファイルレベルに書かれた | は下の「縦線」の規則どおり線になる(書かれていなければ 1 区画のままである)。
単一ファイルの図でも包む。 包むかどうかをフラグで変えると、同じソースが 2 通りの綴りで出ることになる — ファイルレベルの | が、渡した引数によって線になったり字面になったりする。
箱の直前にパスを置く。 どのファイルの箱かは、前置予約語の修飾と同じ位置に、パスの字面として置く。綴りは import に書かれた文字列リテラルそのもの("math.hika")で、入口ファイルはコマンド行に渡された綴りである。図のための名前を作らない — 読み手はその綴りでソースを grep する。
conduit / loop / refinement の修飾は、書かれたとおりの字面として箱の直前に残す。箱にマークを付け足さない。
箱は区画の縦の積みである。 ソースに書かれた括弧 1 つは区画 1 つの箱になり、区画を 2 つ以上持つのは次の「横線」が挙げる 2 つだけである。
縦線 — 書かれた区切り
箱の区画を左右に割る。区切りは 2 つあり、どちらもソースに書かれた記号がそのまま線になる。字面としては残さない — 線が既にその位置を示しているので、同じことを 2 度描くことになる。
| 区切り | 左 | 右 |
|---|---|---|
リテラルの | |
スロット節 | 本体 |
match のアームの => |
パターンとガード | アームの本体 |
書かれていなければ引かない。 | も => も持たない { }(match のパターンに書かれたレコード、素のブロック)には縦線が立たない。
片側が空のときは空欄をそのまま残す。{| body } は左が細い空欄になり、{ slots |} は右が細い空欄になる。「スロットなし」「本体なし」といった語を書き足さない — 空いていること自体が答えである。
縦線の位置は箱に 1 つである。 match のアームを積んだ箱では、左欄の幅は全アームの最大に揃い、縦線は 1 本の直線として通る。揃わなければアームの表として読めない。
スロットは場所が空いていれば縦に並ぶ
スロット節の , は、縦に並べても箱が高くならないときだけ改行になる。 区画の高さは左右の欄の大きい方で決まるので、右の本体がスロットの数分の高さを既に取っていれば、左を縦にしても箱は 1 行も高くならない — 空いている場所を使うだけである。
高さは行数ではない。 match e { … } のように 1 行の中に箱が入っていれば、その行は箱の中身分の高さを取る。行を数えるだけでは「右は短い」と読み違えて、スロットを横に残してしまう。箱の余白は数えず少なめに見積もる — 少なく見積もれば「並べない」側へ倒れるので、箱が高くなることはない。
- 高くなるなら横のままにする。
{ st, dir | … }のような 2 つのスロットの短いリテラルは実コーパスの 4 分の 1 を占め、縦にすると頻出形が軒並み高くなる。得るもの(左欄が細くなる)より失うもの(図が縦に伸びる)が大きい - 効くのはスロットが多く、型注釈で左欄が横に長いリテラルである。左欄が細くなる分、縦線が左へ寄って図の幅が縮む
- 区切りの
,は落ちる。 改行が既にその位置を示しているので、同じことを 2 度描くことになる(まとめたifの引数を区切る,と同じ扱いである) matchのアームの左は数えない。 あちらはパターンであり、そこに書かれた,は OR パターンや値構成子の引数の区切りであってスロットの区切りではない。字面ではなく字句で数えるので、文字列の中の,({ sep := "," | … })を取り違えることもない
横線 — 区画の境目
箱が区画を 2 つ以上持つとき、その境目に横線を引く。区画が 1 つの箱には引かない — 箱の縁が既に上下を閉じているので、そこへ足す線は何も割らない。
区画が増えるのは次の 2 つに限る。
matchのアームブロック — アーム 1 本が区画 1 つになる。=>の縦線と合わせて、アームの表になるifの 2 分岐 — then と else が1 つの箱の 2 区画になる。分岐は 2 つで 1 つの選択なので、離れた 2 つの箱として描くと読み手が対にする手間を負う
if は構文ではなく 3 スロット必須の組込である (language-spec.md §3.9) から、まとめるのはprelude の if を 3 引数で呼び、then と else の両方が { } である位置に限る。並置形 if (c) { a } { b } とタプル形 if(c, { a }, { b }) は同じ意味なので同じ形にまとめる。書き手が if を rebind していればそれは分岐ではないのでまとめない(分類は「名前の色」と同じく compiler/highlight の名前解決をそのまま使う)。when は else を持たないので、区画は 1 つのままである。
まとめた後も then と else は別のリテラルであり続ける — 名前の色が数える捕獲の単位は区画であって箱ではない。
then と else の間にコメントが書かれていたらまとめない。 まとめると 2 つの箱の間の字面は図から落ちる(区画は箱の中身だけを指す)。引数を区切る , はまとめた形が既に表しているので落ちてよいが、コメントは書き手の字面であり、落とせば図から黙って消える。1 つの箱にする見た目より、書かれたものが残る方が先である。
アームの外に書かれたコメント
アームとアームの間、および最初のアームより前に書かれたコメントは、左右に割れていない区画としてその位置に立つ。
どちらかのアームへ寄せない。コメントは上のアームへの注記としても下のアームへの前置きとしても書かれるので、寄せると一方の書き方では誤った帰属として出る。左欄へ寄せるのはさらに悪く、左欄はパターンの幅なので縦線がコメントの幅まで飛び、揃えた意味が消える。書かれた位置にそのまま立てるのが、どちらの書き方も歪めない唯一の置き方である。
アームと同じ行に書かれた後置コメント (1 => 2 # note) はそのアームのものなので、区画は増えない。
名前の色 — 宣言と参照
識別子は 5 つに塗り分ける。分類は compiler/highlight の名前解決をそのまま使い、図のために別の解決を書かない。
| 分類 | 意味 |
|---|---|
| 宣言 | その位置で名前が導入されている(スロット宣言・ローカル束縛・内部名・パターン束縛) |
| 自箱参照 | その参照を囲む最内のリテラルの中で宣言された名前を指している |
| 捕獲参照 | 参照を囲む最内のリテラルより外で宣言された名前を指している |
| prelude 参照 | rebind されていない prelude 大域名 |
| 未解決 | 分類できなかった名前 |
型の位置の名前も同じ 5 つで塗る。 型注釈・enum の変異・matches の右辺に書かれた名前も、指し先が引ければ参照として塗り、宣言との連動に載る。型の位置だからといって別扱いにすると、type Meters := … と m: Meters が図の上で繋がらない — 「この名前はどこを指すか」に答えないことになる。
enum の変異はその位置の宣言である。 enum Color := OneOf(Red, Green) の Red / Green は、そこで名前が導入されている。参照として扱うと、同名の無関係な束縛が居ればそれを指す参照になり、居なければ指し先が無く色も付かない — 分解 ({ Red, Green } := Color) を書いたかどうかで同じ綴りの見え方が変わる。
型語彙(Int・OneOf など)は塗らない。 prelude 参照に数えるのは値として引ける大域名で、型語彙は値ではない。地の色のまま置く。
段数は持たせない。 「何段外か」は読み手の判断を変えないが、捕獲かどうかは変える — 捕獲はリテラルの等価が比べる 3 つの要素の 1 つであり (language-spec.md §9.1)、copy が何を張り直して何を繋いだままにするかを決める。正確な指し先は次のホバーが答える。
名前にカーソルを乗せると、同じ束縛の宣言と全参照が同時に光る。これが「どこを指すか」に対する正確な答えであり、線は引かない。--svg 出力ではこの連動は働かない。
折りたたむ — 中身が複数行のリテラル
中身が複数行のリテラルは、右上のマークで折りたためる。 既定は開いた状態で、折りたたむと中身が隠れて箱が 1 行分の高さになり、その位置に … が出る。図が縦に長くなったとき、今読んでいない箱を閉じて全体の形を見るための操作である。
- 折りたためるのはリテラルの箱だけである。 分解の左辺
{ a, b } := …や添字の[ ]は箱として描くがリテラルではないので、折りたたむ対象にしない。ファイルも暗黙のリテラルなので折りたためる(「箱」) - 折りたたんで縮む箱にだけマークを出す。 中身が 1 行の箱は折りたたんでも高さが変わらないので、押しても何も起きないマークを置かない
- 折りたたんだ状態は図に残らない。 出力はいつも開いた状態で、折りたたむのは読み手の操作である。したがって「決定性」は折りたたみに影響されない
--svg出力は script を持たないのでマークそのものを出さない(ホバーの連動と同じ)。押しても何も起きないマークは、読み手には壊れて見える
折りたたんだ後の位置はページが決め直す。 共有した 1 枚の HTML に処理系は居ないので、折りたたむ操作をこちらで起こすことはできない。ページが持つのは高さの足し合わせだけで、字の幅も箱の余白も測らない — それらは生成側が決めた値のまま <svg> の属性として渡る。噛み合わせ(生成側の値とページの計算が食い違っていないこと)は実ブラウザーの試験が「折りたたんで開けば元の座標に戻る」ことで押さえる。
宣言へ送る — 参照のクリック
名前をクリックすると、手前の宣言へ送る。 ホバーが「どこを指すか」を光らせるのに対し、こちらはその位置まで画面を動かす。ファイルを跨いだ宣言へも同じ仕組みで着く(「ホバーはファイルを跨ぐ」)。
- 送り先はクリックした位置から遡って最も近い宣言である。群に宣言が 2 つ以上入ることがある(
importした名前と、相手ファイルの宣言)ので、import行を踏めばもう 1 段先の宣言へ着く — レキシカルに遡る感覚と同じである - 送り先が折りたたまれた箱の中なら、開いてから送る。 開かずに送ると、見えない位置へ送ることになる
- 着いた先は一瞬だけ光る。新しい色は使わない — ホバーの色をそのまま使う
--svg出力では働かない(ホバーの連動と同じ)
点線の下線 — 書かれた型注釈
型注釈が書かれている名前には、名前の下に点線を引く。注釈そのものはカーソルを乗せたときに出す。HTML 出力はページが自分で描く — 既定のツールチップは約 1 秒待たされるうえ、ブラウザーの外側に描かれるため自動試験で観測できない。--svg 出力は script を持たないので <title> のままで、ブラウザーの既定のツールチップに出る。
対象はソースに書かれた注釈だけである。静的型検査が推論した型は図に出さない(「図が持たないもの」)。gradual な言語では「書き手が注釈を付けた位置」がソースの事実であり、図はその事実を保つ。
下線を引く先
注釈がかかっているものに引く。名前に付いた注釈(スロット宣言・ローカル束縛・内部名)は名前の下に引き、式に書かれたアスクリプションはその式そのものに引く。
位置で選り好みしない。 body の末尾 ({| (x + y): Int }) も、:= の右辺 (init := { … }: State) も、match のアームの本体も、書かれた注釈であることに変わりはない。位置で扱いを分けると、同じ綴りが「下線」と「字面」の 2 通りに描かれる。読み手が覚える規則は書かれた注釈は下線とホバーの 1 つで足りる。
代入 (=) に書いた注釈も名前に付く。 name: 型 = expr は既存の可変束縛への代入であって名前を導入しない (language-spec.md §6.1) が、注釈が掛かっている先は名前なので、:= と同じにまとめられる。
かかっている式は名前 1 つとは限らないので、その式を組み立てる要素へまとめて載せる。
| かかっている式の形 | 下線 |
|---|---|
字面({ x, y | (x + y): Int }) |
その字面の下に点線 |
箱({ n | { count := n |}: Int }) |
箱の下辺を点線にする |
両方を含む({ s | match s { … }: Int }) |
match s の下と、アームブロックの箱の下辺 |
箱にかかった注釈は箱の中身へは重ねない。 箱を丸ごと表明する注釈は箱の下辺に出るので、中の字面にも引けば同じことを 2 度描くことになる。箱の外にある末尾式の字面(match s の match と s)には引く — あれは箱ではなく式の一部である。
掛ける先が無い注釈はまとめない。 名前に付いた注釈のうち、名前が図に色として立たないもの(highlight が解けなかった名前)は字面のまま描く。まとめれば注釈が図のどこにも残らない — 隠せなかったことは読み手に見えるが、消えたことは見えない。
注釈そのものが { } / [ ] を持つ関数型(inc: {| Effect(Int, Mut) } := … など)は、箱の条件も下線の条件も同時に満たす。この場合は隠しが箱に勝つ — 箱として描けば、読み手が下線で隠せと言ったものが画面に残ってしまい、隠す意味が無くなる。
ファイルを跨ぐ
--imports を渡すと、入口ファイルから import を辿って届いた .hika を 1 枚の図に載せる。ファイルは箱なので(「箱」)、増えるのは箱の数だけで、読み手が覚える綴りは 1 つも増えない。
辿るファイル
辿るのは import のパス文字列に書かれた相対パスだけである。1 枚に載る集合を決めるのはソースに書かれた import であり、図が別の規則(ディレクトリの走査など)を持つことはない。
std:/pkg:は辿らない。 前者はホストが提供する実体でソースが存在せず、後者はマニフェストによる解決という別系統である (language-spec.md §13.4)。importの字面はそのまま残り、そこから引いた名前の色も変わらない- 同じファイルは 1 度だけ載せる。 同一性は解決後の絶対パスで見る(§13.4 のキャッシュと同じ単位)ので、
"math.hika"と"./math.hika"は同じ 1 つの箱になる。循環 import も同じ規則で止まる - 箱の直前に置くパスの綴りは、最初にそのファイルへ届いた
importに書かれたものである。同じファイルを別の綴りで指すimportが他にあっても、箱は増えない - ファイルとファイルの間は 1 行空ける。 箱が続けて積まれると、パスの綴りが上の箱の底に貼り付いて「どちらの箱の名前か」が読みにくい。空行 1 つで足りるので、余白のための数を新しく持たない
- 届いたファイルが読めない・パースできないときは図を 1 バイトも出さない(「終了コード」)
ホバーはファイルを跨ぐ
import が束縛するのは相手ファイルの member そのものなので、import した名前と相手の宣言は 1 つの束縛である。カーソルを乗せると、両方のファイルの宣言と全参照が同時に光る。別の束縛として扱えば、図は「同じもの」を 2 つに割って見せることになる。
| 乗せる先 | 光るもの |
|---|---|
分解束縛 import { sqrt } := "math.hika" の sqrt |
相手の sqrt 宣言と、相手の中の全参照 |
丸ごと束縛 import m := "math.hika" に対する m.sqrt の sqrt |
同上 |
パスの文字列 "math.hika" |
相手のファイルの箱に置かれたパス |
m.sqrt を結ぶのに見るのは、m が丸ごと束縛の import で導入された名前かどうかだけである。静的型検査の結果は使わない — 図が出すのはソースに書かれた事実だけ、という下線の規則(「点線の下線」)と同じ線引きである。
線は引かない。 指し先はホバーが答えるので、箱と箱を結ぶ線は同じことを 2 度描くことになる(「何を図にして、何を図にしないか」)。--svg 出力ではこの連動は働かない。
結び先が無ければ結ばない。 相手が export していない名前、std: / pkg: から引いた名前は今までどおりに塗る。図は診断を出さない — 出す口は hikari check が既に持っている。
跨ぎの結び付けが見るのは import / export が定める公開 member 集合 (language-spec.md §13.2) だけである。ファイルの中の名前解決は今までどおり compiler/highlight に訊く(「名前の色」)ので、解決が 2 通りに割れることはない。
決定性
出力はバイト単位で決定的である。 同じ入力からは常に同じ図が出る。
- 箱の寸法・位置・区画の高さ・縦線と横線の座標は、すべて生成側が計算して SVG の座標として書き出す。ブラウザーの流し込みに依存する部分を持たない
- 文字幅は ASCII を等幅・非 ASCII を全角として数え、
<text>にtextLengthを与えて固定する。したがって閲覧環境のフォントが変わっても図の形は変わらない - 走査順は AST の出現順(深さ優先)に固定する。ファイルの並びも同じで、入口からの深さ優先の到達順に固定する(
--imports)
この性質により、.hika と期待される出力の対を突き合わせる試験が書ける。
終了コード
| 状況 | code |
|---|---|
| 生成成功 | 0 |
| 構文エラー | 1 |
| I/O エラー / 引数不正 | 2 |
構文エラーの診断は stderr へ、他のソース系サブコマンドと同じ書式で書く。図は部分的に出さない — パースできないファイルに対しては何も stdout へ書かない。
--imports で辿った先も同じ表で見る。どれか 1 つでも構文エラーなら図は出ない — 部分的な図を出さない規則は、ファイルの数が増えても変わらない。辿った先が読めないときは I/O エラー(2)である。
図が持たないもの
次は図の定義から出る性質で、実装が進んでも変わらない。
- 推論された型 — 出るのは書かれた注釈だけ。型検査の結果を図に載せない
- 図からソースへの復元 — 図はソースの表示であって、可逆な交換形式ではない。図を編集してソースへ書き戻す経路は持たない
将来枠
次は入れる余地があるが、今は持たない。制約として読者が知る必要があるものだけを挙げる。
- ディレクトリ —
pathは入口 1 ファイル。ディレクトリ再帰も、複数図の索引生成も持たない。1 枚に載る集合を決めるのはソースに書かれたimportだけである(「辿るファイル」) - 依存グラフの図 — ファイルとファイルを結ぶ線・矢印は引かない。指し先はホバーが答える(「ホバーはファイルを跨ぐ」)
std:/pkg:の中身 — 辿らない。そこから引いた名前は今までどおりに塗られる(「辿るファイル」)- LSP への搭載 — 図を出す口を language server プロトコルの上に持たない。エディターからは拡張が
hikari diagramを起動して受け取る(「エディターからの表示」)
周辺ツールとの関係
言語の構文・記号・型語彙を変えたときは、本書の「図の綴り」も追従の対象である(CLAUDE.md「周辺ツールも追従」)。ただし図は独自の記法を持たないため、追従が要るのは次の 2 点に限られる。
- 新しいリテラル形(箱になるもの)が増えたとき
- 名前を導入する位置が増えたとき(
compiler/highlightの分類が先に追従する) - 分岐の綴り(
matchのアーム・ifの呼び出し形)が増えたとき(「横線」が数える形が変わる)
エディターからの表示
VS Code 拡張(tools/vscode-hikari)はコマンド 1 つでこの図をパネルに出す。拡張が持つのは表示だけで、図を組む面を別に持たない — hikari diagram を起動して受け取ったページをそのまま webview へ渡す。したがって図の綴りが拡張側に写ることはない。
映すのはディスク上の内容で、保存に追従する。
開く口は 3 つある。 コマンドパレット・エディターのタイトルバー・エクスプローラーの右クリックで、いずれも .hika のときだけ出る。右クリックから呼ばれるときは選ばれたファイルの URI が引数で渡るので、エディターを開いていなくても図が出せる(Markdown の Open Preview と同じ操作である)。
import 先を含めるかも同じツールバーで切り替える(下記「コメントの非表示」)。パネルを閉じれば辿らないところへ戻る。構文エラーのときはページが出ない(本書「終了コード」)ため、拡張は直前の図を残して診断だけを別に見せる。
コメントを出すか・import 先を含めるかは、図の上に出るツールバーのチェックボックスで切り替える。押すとその場で図を組み直す — どちらも図の中身が変わる指定なので(コメントを落とすと箱の幅が変わり、import を辿ると載るファイルの数が変わる)、覚えているページへ被せ物を付け替えるだけでは足りない。
今の指定がそのままマークとして出ていることが要件である。押すまでどちらか分からない口(状態を出さないボタン)は置かない — 読み手は「コメントが無い図」と「コメントを落とした図」を区別できなければならない。
ツールバーはパネルの枠であって図ではない。 拡張は受け取ったページの <body> の直後へツールバーを 1 枚重ねるだけで、図の <svg> には一切触らない。差し込みが空振りしてもツールバーが出ないだけで図は壊れない。
設定 hikari.diagram.comments は図を開いたときの初期値である(既定は出す)。ツールバーは今見ている図にだけ効き、設定は書き換えない — 試しに 1 度落としただけでワークスペースの既定が変わっては、次に別のファイルを開いた人が驚く。パネルを閉じれば覚えた値も消え、次の図は設定の値から始まる。import を辿るかは設定を持たない — 辿るかどうかは「今何を読みたいか」の選択であって、ワークスペースの既定として据えるものではない。
Playground からの表示
web の Playground(web/play.html)はエディターのペインと図のペインを切り替える。図を組むのは同じ処理系である — Playground はブラウザーの中で処理系そのものを走らせるので、拡張のように別の処理を起動するのではなく、今編集している綴りをその場で図にする。
拡張との違いは受け取る形だけである。拡張はページを丸ごと webview へ渡すが、Playground は既に自分のページを持っているので、<svg> と「包む部品のエクスポート」の 2 つを受け取って自分のページへ載せる。ページの地は Playground のものを使い、図の明暗はルートの data-hikari-theme でページへ揃える(「明暗の明示」)。
コメントを出すか・import 先を含めるかは、図のペインの中に出るツールバーのチェックボックスで切り替える。押すとその場で図を組み直し、今の指定がそのままマークとして出ている。要件は拡張のツールバー(「エディターからの表示」)と同じで、押すまでどちらか分からない口は置かない。ツールバーは図のペインの枠であって図ではない — 図の <svg> はツールバーの下の枠にだけ入る。
指定は控えへ据えて渡す。 ブラウザーへ出す入口は「文字列 1 本を受けて文字列 1 本を返す」形に固定してあるので、指定を引数としては渡せない。可否ごとに入口を分けると、独立した可否が増えるたびに入口が倍になるので、据える口を 1 つ持つ形にする(同梱ファイルの束と入口の名前が既に同じ形である)。ソースの先頭へ指定を紛れ込ませる形は採らない(綴りが図の入力そのものと混ざる)。
映すのは編集中の内容 1 本で、図のペインへ切り替えた時点の綴りを図にする。構文エラーのときは図が出ない(本書「終了コード」)ため、Playground はペインを切り替えず、診断を静的検査と同じ出力ペインへ出す。図が黙って古くなる形を作らないよう、綴りを書き換える操作(整形・例の読み込み)はエディターのペインへ戻す。
文字の大きさはページの指定に従う。 ページが持つ拡縮(A− / A+)は図にもかかる。掛けるのは図まるごとに同じ倍率で、図の中の寸法は 1 つも書き換えない — 字送りは textLength で確定させてある(「出力」)ので、字だけを拡げれば字送りとずれて図が歪む。
辿る先はこのページが同梱したファイルだけである
ブラウザーに OS のファイルシステムは無いので、import を辿れるのはページが一緒に配ったファイルに限られる。Playground の「パッケージ例」がこれで、入口のほかに相対 import の相手を積んでいる。辿る規則そのものは --imports と同じである(「ファイルを跨ぐ」)。
- 同梱を持たない綴り(利用者が書いたコード・単一ファイルの例)では、
import先の口を閉じる。押しても何も起きない口も、押すと図が消える口も置かない — 辿る先の無い相対importは「届かないファイル」になり、図が丸ごと出なくなる(本書「終了コード」) - 同梱の中に届かないファイルがあるときは、
--importsと同じく図を 1 バイトも出さず、理由を静的検査と同じ出力ペインへ出す - 同一性はルートからの相対パスで正規化した綴りで見る。ブラウザーには作業ディレクトリが無いので、絶対パスへ直す段は挟まない(
"m.hika"と"./m.hika"が 1 つになるのは--importsと同じである)