Skip to content

要件定義: loom を日常使用に載せる

この文書は、実装予定表ではなく、日常使用に必要な判断とその理由を記録する。 実装状態は現在のコードと検証結果に合わせて更新する。証拠のためだけの古い file:line は残さない。

1. 判断基準

loom は SBCL 上の Common Lisp で動く Emacs 風ターミナルテキストエディタである。 判断基準は、作者が毎日使えることと、実機の入力・描画経路まで動くことである。

残す機能は作者が使う、または使う意思があるものに限る。削る機能は、実装量よりも event loop、状態、永続化形式、回帰テストを増やす保守負担を理由に判断する。

対象言語は Common Lisp / Emacs Lisp、Nix、TypeScript / Web、Markdown / Org とする。 Lisp 系では実行中の定義、マクロ展開後の状態、REPL との接続が必要なので SWANK を 使う。Nix と TypeScript ではソース診断、補完、定義ジャンプを言語サーバから得る ため LSP を使う。両者は対象と得意な情報が違い、一方で他方を代替しない。

2. 現在の機能要件

FR-000: multiple-cursors の撤去

状態: 撤去済み。

multiple-cursors は作者が使っておらず、今後も使う意思がない。行単位だけの部分実装を 維持すると、入力分岐、yank、描画、テスト、公開APIを増やし続けるため撤去した。 単一カーソルの insert、kill、yank、yank-pop の経路はこの判断の対象外とし、既存挙動を 保つ。

FR-001: 全角文字を含む行のカーソル位置

状態: 実装済み。

カーソル、mode-line の列表示、ミニバッファは、文字数ではなく cl-tty-kit:char-width を通した表示セル列を使う。全角文字の前にある point が 画面上で左にずれないことを受け入れ条件とする。

The Nix build applies a project-local patch to the pinned renderer. It omits wide-character continuation cells from ANSI output and clears those cells when a wide character is replaced by a narrow one. Regression tests cover full and incremental output, row boundaries, and output-length estimates. These tests do not replace the author's terminal checks for FR-001 through FR-003; see the verification ledger.

FR-002: 横スクロール

状態: 実装済み。

切り詰め表示のウィンドウは point を水平スクロールで可視範囲に保つ。切り出しは 表示セル境界を共有し、全角文字を半分だけ描画しない。

FR-003: 行折り返し

状態: 実装済み。

折り返し表示は論理行を画面セル幅に分け、point、スクロール、カーソルを画面行へ 写像する。next-line と previous-line の移動単位は画面行とする。これは Emacs の line-move-visual が既定値 t のときの挙動に合わせる決定であり、文章を読むときの 上下移動が端末上の一行と一致するためである。

FR-004: 表示方式の切り替え

状態: 実装済み。

コード系の major-mode は切り詰め、Markdown、Org、plain text は折り返しを既定値と する。M-x toggle-truncate-lines は選択バッファの設定を反転する。

表示方式はセッションに永続化しない。表示方式を保存すると session v5 エンベロープを 変更し、reader の互換性判断が必要になる。現行 v5 は据え置きとし、復帰時は major-mode の既定値から表示を再構成する。

FR-005: major-mode

状態: 実装済み。

Common Lisp、Python、Rust、Shell、JSON、Markdown、plain text に加えて、Nix、TypeScript、 Emacs Lisp、Org の構文メタデータを登録する。各モードは拡張子、コメント接頭辞、 indentation、language id、キーワードを持ち、コメント操作と汎用ハイライトから利用する。

Org の範囲は構文メタデータの登録までとする。アウトラインの折り畳み、表編集、babel 実行、Org 専用の編集モデルは未決であり、この要件には含めない。

FR-006: インクリメンタルサーチ

状態: 実装済み。

C-s と C-r は入力中のパターンに合わせて point を移動し、一致箇所を表示する。 RET は位置を確定して履歴へ登録し、C-g は検索開始位置へ戻す。通常の M-x search-forward は確定入力型の検索として残す。

FR-007: S 式移動と対応括弧

状態: 実装済み。

C-M-f、C-M-b、C-M-u、C-M-d、C-M-k を S 式単位で実装する。文字列、コメント、 文字リテラル内の括弧は構造として扱わない。point に隣接する括弧と対応括弧を表示し、 対応が無い場合は誤った位置を表示しない。

FR-008: 構造編集

状態: 実装済み。

slurp、barf、wrap、splice、raise を提供する。各操作は括弧の対応を保ち、単一の undo 単位とする。

FR-009: SWANK 接続

状態: 未実装。

理由はソケット層が存在しないことである。次の確認を現行ツリーで再実行し、出力が空で あることを受け入れ条件とする。

grep -rln 'socket\|usocket\|sb-bsd-sockets\|swank' src packages

LSP は標準入出力を使う外部プロセス接続であり、TCP で待ち受ける SWANK の基盤には 流用できない。SWANK を実装するときは、LSP とは別の infrastructure 境界、接続失敗と 切断の状態、外部 SBCL イメージへの評価経路を追加する。

FR-010: LSP 補完と定義ジャンプ

状態: 実装済み。

実サーバとの initialize、document sync、diagnostics、completion、definition、jump origin への復帰を実装する。server capability が無い要求は送信せず、結果をミニバッファ、 popup、または対象バッファへ反映する。

3. 実機検証で記録した配線切れ

in-process テストが緑でも、raw-mode のキーマップ、外部プロセスの初期化、画面復元を 通る実機経路が切れていれば機能は使えない。この run では次の4件を実PTYで発見し、 原因を修正した。

機能 症状 原因と処置
terminal RET 後の入力が子シェルへ届かず read-only エラーになった RET が newline-command に解決され、端末入力分岐の (null command) 条件を満たさなかった。端末用の特殊入力と子プロセスが受理する enter を分けた。
lsp-completion-at-point session が無いという表示になった session は存在したが initialize 応答を drain する前で initialized-p が偽だった。要求コンテキストで初期化を待ち、main loop の LSP polling も有効にした。
lsp-find-definition completion と同じ session エラーになった 同じ initialize 応答の処理漏れだった。completion と共通の初期化処置で直した。
set-mark-command (C-SPC) mark が設定されなかった 素の端末が送るイベントは :NULL だが、実装とコメントは CONTROL-@ を想定していた。C-SPC の raw event を mark command へ接続した。

この4件が示す判断は、実装シンボルを呼ぶテストだけでは入力の解決、外部プロトコルの ライフサイクル、端末画面の状態を証明できないということである。機能を「実装済み」と 記録するには、該当する実PTY経路か、同じ境界を通る検証を残す。

4. 非機能上の判断

  • 表示幅とカーソル幾何は presentation / infrastructure に置き、packages/core/editor の domain 層へ端末概念を持ち込まない。
  • 表示セル幅の根拠は cl-tty-kit:char-width に統一する。カーソル、描画、折り返し、 横スクロールはこの計算を共有する。
  • セッション reader は v5 エンベロープを受理する。表示方式は保存しないため、この 判断だけでは v5 を上げない。
  • 実PTY E2E は実サーバを使う。LSP の確認に偽サーバを差し替えない。
  • 実行ゲートは入力が空でないことを確認する。E2E はシナリオと command-spec の被覆を 照合し、in-process は failed、errored、skip の件数を出力から読む。

5. 今後の境界

FR-009 の SWANK、LSP の動的登録や追加リクエスト、完全な VT 端末エミュレーション、 Org のアウトラインと表編集は、現在の毎日使う編集経路を置き換えずに別課題として扱う。 新しい機能を追加する前に、raw-mode の入力と描画、外部プロセスの終了処理、session v5 との関係を要件に記録する。