sim にいまできること

sim がいまできることを、目的ごとにまとめました。各機能には、裏づけとなる仕様の要件 ID を添えています。予定の機能はロードマップにだけ載せています。

隠れたものを見せる

中心にある考えです。ファイルを、ありのままの姿で見せます。

  • 不可視文字と双方向制御文字を見せる

    右から左への上書き文字、ゼロ幅スペース、制御文字は、<U+202E>、<U+200B>、^[ のようにマゼンタのブロックで描かれます。選択中、検索のマッチ、カーソル行、外部からの変更の中、遠くの行のフォグの中など、セルがどんな状態でも同じです。端末にそのまま送られることは一切ありません。ファイルに書かれた文字列 <U+202E> にはブロックがつかないので、両者を見分けられます。

    仕様: SAFE-8, UI-15

  • 何も見えない空白に印をつける

    行末の空白は ·、ノーブレークスペースは ⍽、タブは » で描きます。どれもコードで使われない記号で、本文の上に重ねて描くだけなので、テキストには含まれません。行末記号は描かず、- や % も使いません。コードで普通に使う文字だからです。

    仕様: UI-6

  • 外部からの書き込みに印をつける

    sim は開いているファイルを監視します。ビルドやフォーマッタ、ツールなど別のプロセスがファイルを書き換えると、本文の変更行に印がつき、枠にディスク上の変更数が出て、タブに ↯ がつき、ツリーのファイルにも印がつきます。保存のときは、まずファイルのハッシュを比べ、読み込んだあとに変わっていれば何も書き込みません。ただし、確認と同じ瞬間に書き込まれた変更は、すり抜ける可能性があります(「セキュリティ」を参照)。

    仕様: DISK-1, DISK-2, DISK-6, SAFE-5, UI-4, MARK-1

  • git HEAD との差分がひと目でわかる

    追加・変更・削除された行の記号、ツリーの各ファイルのステータス文字(M、A、U、D、R)、フォルダには配下でもっとも強いステータスの文字、ブランチ名とステータス行の +n ~n -n。いずれも git から取得し、バックグラウンドで実行するので、キー入力の処理を止めません。

    仕様: UI-3, UI-5, UI-10

  • リポジトリ自身の設定では、git にコマンドを実行させられない

    git を実行する前に、sim は git にリポジトリ自身の設定を尋ねます。コマンドラインで止められるものは無効にします。ファイルシステムの監視、フック、フィルタ、外部 diff、テキスト変換は何も実行せず、無効にした設定の数はツリーに出ます(2 git settings off)。それでも、止められないコマンドを実行しうる設定(core.sshCommand、それを持ち込みうる include など)がある場合は、git を実行しません。ツリーには git の印が出ず、git not run と出て、ステータス行には原因のキーとファイルが出ます。git はシェルを通さず、オプションのロックなしで、取得(fetch)もできない形で実行します。

    仕様: MARK-1, GIT-1, GIT-2, GIT-3, GIT-5

  • 何も描かない文字は、すべて見える形で表示します

    右から左への上書き文字やゼロ幅スペースに加えて、タグ文字(AI アシスタントに指示をこっそり渡す手口)、異体字セレクタ(公開された拡張機能にコードを隠した手口)、ハングルのフィラー、ソフトハイフン、行区切り、未割り当てのコードポイントも、<U+E0041> のように描きます。対象は、ファイルの本文、ステータス行、メッセージ、ツリーやタブの名前など、sim が文字を描くところすべてです。絵文字の連なり、キーキャップ、イングランド・スコットランド・ウェールズの旗、Nerd Font のアイコンは、そのまま描きます。漢字の後ろに字形を指定する異体字セレクタがある場合は、そのセレクタを表示します。

    仕様: SAFE-10, SAFE-11

  • 注意のブロックは、文字だけに使いません

    マゼンタは、よく見るべきものを示します。隠れた文字のほか、テキストが示すアドレスとリンク先が食い違う Markdown のリンク([https://good.example](https://evil.example))は、リンク先がこのブロックの上に描かれます。安全のために git を実行しなかったリポジトリでは、ツリーのブロックに git not run と出ます。

    仕様: UI-15, MD-10, GIT-5

拒むべきものは拒み、テキストは失わない

明示的な操作なしには誰のテキストも失われず、ファイルがエディタの設定を書き換えることもできません。

  • モードラインも、設定ファイルも、プラグインもない

    設定やコマンドをファイルの中身から読むことはないので、ファイルがエディタの設定を書き換えることはできません。リポジトリから読み込む設定ファイルも、プラグイン API もなく、sim は自分ではネットワーク接続を開きません。記憶するのは、見た目、アイコン、Markdown の表示方法の選択だけで、どれも 1 語です。

    仕様: SAFE-9

  • 書き込みが中途半端にならない保存

    保存では、元のファイルと同じ場所に一時ファイル(モード 0600、O_EXCL)を作って書き込み、fsync し、元のファイルへ rename し、ディレクトリも fsync します。見えるのは古いファイルか新しいファイルのどちらかで、混ざることはありません。他人が所有するファイル、ハードリンクのあるファイル、書き込めないディレクトリのファイルは、復旧用コピーを作ったうえでその場で保存します。他人が所有するリンクをたどった書き込みは拒否します。編集せずに開いて保存した場合、出力はバイト単位で同一です。

    仕様: SAFE-1, SAFE-2, SAFE-3, SAFE-5

  • クラッシュしてもテキストを失わない

    未保存の変更は、最短で 1 秒おきに、他人から読めないステート用ディレクトリ(0700)へスナップショットとして保存します。クラッシュ、kill -9、端末を閉じたあとにファイルを開き直すと、取り消し 1 回分として復元を提案します。失われる作業は最大でも 1 秒分です。

    仕様: SAFE-6

  • 検索で、リポジトリが指定するコードを実行しない

    sim は PATH の絶対パスの項目から ripgrep を自分で探し、環境変数を空にして --no-config つきで実行し、--pre は決して渡しません。そのため、リポジトリが検索にコードを実行させることはできません。検索は何も書き込まず、プロジェクトの外に出るシンボリックリンクもたどりません。

    仕様: GREP-7, GREP-8

  • テキストでないファイルは読み取り専用で開く

    有効な UTF-8 でないファイル、先頭 8 KB に NUL があるファイルは、理由を示して読み取り専用で開き、不正なバイトは U+FFFD で描きます。画面のテキストがファイルそのものではなくなってしまうので、編集と書き込みは拒否します。

    仕様: SAFE-4

  • 競合する保存では、両方のバージョンを残す

    保存しようとしてファイルがディスク上で変わっていたとき、どちらのテキストも失われません。:w! は、相手のバージョンをステート用ディレクトリへコピーしてから自分のものを残し、:e! は、自分のバージョンのスナップショットを取ってから相手のものを取り込みます。

    仕様:

  • --plain: 外部プロセスを実行しない

    sim --plain は中核だけの sim です。calm の見た目、ファイルは 1 つ、パネルなし、そして自前のプログラムは一切実行しません。git も ripgrep もフィルタもフックも動かしません。root で実行したときと、sudoedit のエディタとして動くときの既定で、ステータス行に plain と出ます。

    仕様:

たどり着く速さ

「あのファイル」から、読み始めるまでの手数をできるだけ減らします。

  • 動かないプロジェクトツリー

    sim を起動すると、カレントディレクトリのツリーが開きます。ディレクトリが先、ファイルがあと、それぞれ名前順で、並びは常に同じです。展開の状態も残るので、ファイルを読んで探すのではなく、位置で覚えて探せます。ディレクトリはバックグラウンドで読み、届いた項目は上の行を動かさずに現れます。

    仕様: TREE-1, TREE-3, TREE-4, TREE-5

  • 浮かぶウィンドウでファイルとテキストを探す

    Ctrl-p はパスのあいまい一致でファイルを探し、一致の良さと、次に開きそうな度合いで並べます。Ctrl-f は入力するそばから全ファイルの中身を検索し(PATH に ripgrep があればそれを使い)、結果をファイルごとにまとめ、選んだ行をプレビューします。Esc で、すべて元の状態に戻ります。

    仕様: FIND-2, FIND-4, GREP-1, GREP-5, PICK-1

  • 次に開きそうなファイルに、その場で印をつける

    sim は開いたファイルを記憶し、直近さと頻度の両方で順位づけます。上位 5 つにはツリーで • がつきます。. を押すと、変更されたファイル、開いているファイル、最近開いたファイルだけにツリーを絞り込みます。

    仕様: MARK-2, MARK-3

  • 複数のファイル、それぞれが完全なエディタ

    開いているファイルごとに、取り消し履歴、スナップショット、監視、保存を持ちます。タブバーに並び、未保存の変更には太字の ● がつきます。Tab と Shift-Tab で切り替えます。

    仕様: BUF-1, BUF-2, UI-4

  • 気づかないほど速い

    プロジェクト自身のベンチマーク(リリースビルド、10 万行のファイル、パネルを開いてハイライトを有効にした状態)で、1 キーの処理は 80×24 から 240×60 までの画面で、p99 が 1 ms 以内です。10 万ファイルのリポジトリでは、ファイル一覧を読む前に最初のフレームを描き、実測で 1.8〜8.7 ms(要件は 20 ms)でした。ツリーの走査、git への問い合わせ、ハイライトの構文解析など、時間のかかる処理はバックグラウンドのスレッドで行います。

    仕様: SPEED-1, SPEED-2

  • バイナリは sim ひとつ

    別々だった si を sim に統合します。インストールするコマンドも、起動するコマンドも 1 つです。中核はライブラリの sim-editor-core で、sim はその上に作られています。sim --version と sim --help は、端末に触れずに表示して終了します。

    仕様:

  • 文字でタブを選ぶ

    <Space>bp で各タブに文字が付き、その文字を押すとそのファイルに移ります。<Space>bc は同じ方法でタブを閉じますが、変更のあるファイルは閉じません。2 つ目のファイルを開いたとき、Tab と Shift-Tab のヒントを、3 回使うまで表示します。

    仕様:

  • git の HEAD との変更をまとめたツリー

    <Space>ge で、HEAD と異なるファイルだけを並べたツリーを開きます。ステージ済み、未ステージ、未追跡のすべてを含み、各ファイルにステータス文字と +n −n、各フォルダには合計が出ます。Enter で、そのファイルの HEAD のバージョンを読み取り専用で開きます。

    仕様: GIT-1, GIT-2, GIT-3, GIT-4, DIFF-1, DIFF-3, DIFF-4

読みやすさ

色とレイアウトは、勘ではなく計算で決めます。

  • 自分の端末のパレットで色づけするシンタックスカラー

    色は端末のパレット(OSC 4 で取得)から取り、色相は保ったまま、背景に対して本文が |Lc| 75 に達するまで明るさだけを動かします。キーワードは赤、文字列と数値は緑、型は黄、関数はシアン、コメントはグレーの斜体で、青とマゼンタはコードには使いません。言語はファイル名、拡張子、先頭行から判定し、bat のシンタックスセットを使います。zh で色づけをオフにできます。

    仕様: SYN-1, SYN-2, SYN-4

  • Markdown の閲覧ビュー

    Markdown ファイルでは、カーソルのある行以外は、読んだとおりの見た目で描きます。強調やコードの記号は描かず、表は表示幅でそろえ、コールアウト(> [!NOTE])は種類の語を表示します。隠すのはそれらの記号だけで、#、かっこ、URL、本文の文字は決して隠しません。<Space>um で元のテキストを表示できます。

    仕様: UI-8, MD-1, MD-10, MD-11

  • 3 つの見た目

    calm は素直な見た目で、4 つのスタイルと 1 色の背景だけを使います。flat は差分を緑と赤で見せ、行に淡い色をつけます。既定の depth は、枠、全体を見渡すルーラー、カーソルからの距離で文字を薄くするフォグを加えます。zl で順に切り替えます。赤と緑の区別がつきにくい方には calm が向いています。

    仕様: LOOK-1, LOOK-2, LOOK-3

  • 測って決めた読みやすさ

    本文は |Lc| 90、副次的なテキストは |Lc| 60(APCA)で、端末の背景色から計算します。calm では、唯一の変更色が、protan(1 型)・deutan(2 型)・tritan(3 型)のどの色覚でも見分けられます。インターフェース全体で、1 つの色相は 1 つの意味だけを持ちます。グレーは構造、青は操作する場所、緑・琥珀・赤は変更、マゼンタは隠れているものか危険なものだけです。

    仕様: VIS-4, VIS-5, COLOR-1, UI-12

  • 住み込めるエディタ

    ツリーのアイコン(Nerd Font が必要で、zi でオフにできます)、タブバー、エディタのタイトルになるパンくず(src › sim.rs › leader_key)、モード・ブランチ・HEAD との差分・位置を出すステータス行(エンコーディングと改行コードは、UTF-8 や LF でないときだけ出します)、相対行番号、インデントガイド、印つきのスクロールバー、ヤンクした範囲の一瞬のフラッシュ。

    仕様: UI-2, UI-5, UI-6, UI-10, UI-11

  • 日本語などの CJK テキスト

    全角文字は 2 桁として数え、文章は日本語の禁則処理で折り返し、単語移動は文字種が変わるところで CJK の文章を区切ります。IME がオンのままでも、全角のキーがコマンドとして働きます。

    仕様: VIS-7, VI-10

  • 色がなくても使え、色だけに頼らない

    NO_COLOR と、8 色未満の端末では、太さ・下線・反転だけで描き、端末に色を問い合わせることもしません。意味を色だけに頼らず、サイン列では変更行を ┆、フォルダは配下でもっとも強い git ステータスの文字、ディスク上で変わったファイルのタブは ↯ で示します。アイコンをオフにしたときは、私用領域のグリフを一切描きません。

    仕様:

  • Nerd Font がありそうな端末で、アイコンを最初からオンに

    アイコンには Nerd Font が必要ですが、端末は自分が持っているかを教えてくれません。そこで、Ghostty、kitty、WezTerm では最初からオン、ほかではオフにして、ステータス行に 1 度だけ zi: icons (needs a Nerd Font) と案内します。zi で最後に選んだ状態が、いつも優先されます。

    仕様:

  • コピーした範囲が、どの見た目でも光ります

    ヤンクしたばかりの範囲は 150 ms のあいだ光ります。これまで光らなかった calm でも、選択範囲と同じ灰色で、選択のないところでは下線で光るようになりました。開いているファイルに別のプロセスがいま書き込んだ行は、400 ms 光ります。

    仕様: UI-13, UI-19

キー操作

vi の文法から、vi の悪癖だけを取り除きました。

  • 手が覚えている vi のキー

    モーション、オペレータ、テキストオブジェクト、カウント、.、ビジュアルモード、smartcase の検索、:s と範囲指定。vi の悪癖は除いてあります。Y は行末までのヤンク、U はやり直し、Ctrl-s で保存、q で終了です。矢印、Home、End、Delete はどこでも使えます。

    仕様: VI-1, VI-2, VI-3, VI-4, VI-5, VI-6, VI-7

  • ヤンクはシステムのクリップボードへ

    レジスタ名のないヤンクはすべて OSC 52 でシステムのクリップボードにも送られ、tmux の中でも同様です。削除と変更はレジスタにだけ入るので、x や dd で、コピーしたものが上書きされることはありません。

    仕様: VI-8

  • 驚きのないマウス

    クリックでファイルを開いたりカーソルを置いたり、ホイールでスクロール、ドラッグで選択ができます。マウスだけで、編集、保存、スナップショットが勝手に行われることはありません。

    仕様: PTR-1, PTR-2

  • リーダーキーは Space、which-key のウィンドウつき

    プレフィックス(Space、g、z、Z、[、]、d のような演算子)で少し待つと、次に押せるキーを並べた小さなウィンドウが開きます。そのまま打ち続ければ、ウィンドウがないときとまったく同じに動きます。

    仕様:

  • 検索が今どこにいるかを教える

    検索の一致が点灯しているあいだ、ステータス行にカーソルが何番目で全部でいくつかを [3/12] の形で表示します。

    仕様:

  • Vim のレジスタ

    "a から "z はテキストを保持し、"A から "Z は追記します。"0 は直前のヤンク、"1 から "9 は直前の削除、"_ は何でも受け取って何も保持しません。:registers で内容を一覧できます。レジスタはメモリ上にだけあり、開いているすべてのファイルで共有されます。名前なしのヤンクは、これまでどおり OSC 52 でシステムのクリップボードに届き、削除は届きません。

    仕様: VI-15

  • マーク

    ma から mz で、ファイル内にマークを付けます。'a はマークのある行へ、バッククォートのキーはマークの正確な位置へ移動し、オペレータとも組み合わせられます(d'a)。'' は、直前のジャンプの前にカーソルがあった場所へ戻ります。マークは、編集、アンドゥ、外部からの変更のあいだも、付けたテキストについていきます。:marks で一覧できます。マークはメモリ上にだけあります。

    仕様: VI-17

  • :tutor

    短いレッスンをスクラッチバッファで進める、約 15 分のチュートリアルです。言語はロケールで決まります(English、日本語、简体中文、繁體中文、한국어、Español、Português、Français、Deutsch、Русский。それ以外は English)。練習問題は、入力に合わせて ✗ と ✓ が自動で切り替わります。

    仕様: TUTOR-1, TUTOR-2, TUTOR-3, TUTOR-4

ロードマップ

予定のもので、まだ提供していません。このリストのどれも、いまは使えず、順番は約束ではありません。

  • マクロ

    予定

    キー操作の記録と再生です。いまの sim にはありません。

  • クリップボードとの連携

    予定

    いまの OSC 52 によるヤンクより一歩進んだ連携です。

  • サンドボックス内の言語サーバー

    予定

    言語サーバーのクライアントです。サーバーはサンドボックスの中で動き、プロジェクトのルートを信頼しているかどうかをステータス行に表示します。

  • 本物の奥行きをもつ GUI

    予定

    本物の影と高さ(エレベーション)をもつグラフィカルなフロントエンドです。プロトタイプの段階で、インストールできるものはまだありません。

  • ほかのインストール方法

    予定

    AUR、crates.io のパッケージは近日公開です。