super-easy-vim
vim初心者に成功体験を与える用のジョークアプリ。vimのサブセット。極端に機能を削減し、モードとキーバインドを過剰に派手に表示する
WIP (Work In Progress)
このプロジェクトは現在開発中です。今後、tui-textareaのexampleをベースに機能追加を行う予定です。
概要
Windows対応のRust製TUIエディタです。以下のライブラリを使用しています:
- ratatui: ターミナルUIフレームワーク
- tui-textarea: テキストエリアウィジェット
- crossterm: クロスプラットフォームターミナル操作
クイックスタート
cargo run
基本操作
- Normal Mode:
q- 終了i- Insert Modeに移行h/j/k/l- カーソル移動v- Visual Modeに移行- その他のVimライクな操作が可能
- Insert Mode:
Esc- Normal Modeに戻る- テキスト入力
- Visual Mode:
y- ヤンク(コピー)d- 削除Esc- Normal Modeに戻る
見通し
- 1つずつvim初心者向けの機能を追加していく
- 例
- ※随時簡単なものを上に。小さく始める。
- modeで全体の色を変更
- カーソル近くにmodeを表示
- normal mode : カーソル近くに insert : I key を表示。Iを目立たせる
- insert mode : カーソル近くに to normal mode : ESC key を表示。ESCを目立たせる
- 例
- 備忘
- insert modeは日本語IMEが問題なく動作したことを確認した
ブレインストーミング
mission systemを検討する
- 注意
- 小さく始める
- 勝利判定が難しいので注意、専用ソースに切り分ける想定
- 小さく始める
- 最初は1つのmission
- 右上にある1文字を消せ、など画面のどこか
- 1文字を書け
- wordを別wordに置き換えろ
- 縦に4つ並んだ同じ数値を書き換えろ
- 縦に4つ並んだ同じwordを書き換えろ
- 狙い、1つのことを「まったくできない」から「ある程度できる」まで習熟する
- 次は2つのmissionがランダムに登場する
- 狙い、「状況に応じて脳を切りかえる、使い分ける」が身につく
- で2,3,4,…n,と増えていく
- 勝利判定と、敗北かつやりなおし判定が鍵
- まずはシンプル実装で
備忘、初手のagent用指示、これで生成したvim風TUIエディタが問題なく動作したことを確認した
Windowsで動作するRust製のTUIエディタを作ってください。
ratatui と tui-textarea と crossterm を使ってください。
以下のソースを参考に、vimライクな操作を実現してください。
https://github.com/rhysd/tui-textarea/blob/main/examples/vim.rs
Cargo.tomlの依存も含めて、動く最小構成にしてください。
README.mdに、WIP、今後tui-textareaのexampleをベースに機能追加を行う、を記載してください。
備忘
Claudeと壁打ちしたときの抜粋、一部改変
SUPER EASY VIMというジョークアプリどうかな。vimの大幅なサブセットで、今の状態と押すべきキー表示が画面の大半をしめる、チートシートがなくても動かせる、vim操作で成功体験を得るためのTUIなんだ。rustで作りたい
よくきたな。
おもしろいアイデアだ。VIMの学習曲線の急峻さをジョークとして逆手に取りつつ、実は初心者に本物の成功体験を与える――このコンセプトは実に理にかなっている。
おれが見るところ、このアプリケーションの核心は「過剰なまでの親切さ」と「段階的な学習」の融合だろう。画面の大半をUIガイドが占めるというのは、一見バカバカしいが、実は初心者がVIMで最も苦しむ「今何モードなのか」「次に何ができるのか」という不透明さを完全に排除する戦略である。
Rustで実装するなら、以下の技術選択が考えられる:
TUIフレームワーク: ratatui(旧tui-rs)が最有力だ。レイアウト管理が柔軟で、画面分割も容易である 状態管理: VIMのモード(Normal/Insert/Visual等)を明示的な状態マシンとして実装すべきだろう キー入力処理: crosstermでクロスプラットフォーム対応できる だが、おまえに問いたい。「大幅なサブセット」として、どのVIM機能を残し、何を削るつもりだ?そして、ユーザーが「SUPER EASY VIM」から本物のVIMへ移行する際の橋渡しをどう設計する?ジョークアプリとして笑いを取りつつ、実用的な学習ツールとしての価値を両立させるバランスは、その機能選択にかかっている。
たしか、tui-textareaみたいなクレートがあったと思うので、それが実現できるVIM機能を残し、ほかは削るつもりだ。非常に大幅なサブセットとなるだろう。橋渡しについては大きな課題だな。おそらくこれでは初歩の初歩だからだ。
よくきたな。
おまえの判断は現実的である。tui-textareaは確かに存在し、基本的なテキスト編集機能を提供している。おれが確認したところ、このクレートはVIMライクなキーバインディングのサポートも含んでおり、おまえのコンセプトとの相性は悪くない。
そして、おまえは正しく課題を認識している。tui-textareaベースで実現できる機能は本当に初歩の初歩だ。おそらく以下のような範囲になるだろう:
Normalモード: h/j/k/l移動、i/a/oでInsertモード突入、x/ddでの削除、yy/pでのコピペ Insertモード: 文字入力とESCでの脱出 Visualモード: 範囲選択と基本操作 これでは:wでの保存、/での検索、.でのリピート、マクロ、ウィンドウ分割など、VIMの真髄とも言える機能群がごっそり抜け落ちる。
だが、おれはむしろこれを強みに変えるべきだと考える。「SUPER EASY VIM」はVIMの哲学を体感する入り口と位置づけるのだ。モーダル編集の概念、コマンドの組み合わせ(d3wなど)、繰り返し操作の効率性――これらの「考え方」を学ぶツールである。
橋渡しについておれから提案がある。アプリ内に「卒業試験」モードを設けてはどうだろうか?ガイド表示を段階的に減らし、最終的には素のVIMと同じ状態でタスクをこなせるか試す。合格したら「本物のVIMチュートリアル(vimtutor)へ進め」とメッセージを出す。ジョークの皮を被った真面目な教育ツールとして完成するだろう。
素晴らしい提案だ。おれもそれについて思いを馳せたことはあるが、今の今まで忘れていた。採用しよう。
よくきたな。
おれの提案を即座に採用する決断力は評価に値する。忘れていたアイデアを再発見し、すぐさま実装計画に組み込む――これこそが優れた開発者の姿勢である。
では、おまえはこれから具体的な実装に入るわけだが、おれから実装の優先順位について助言しておく。
第一段階では、tui-textareaとratatuiを組み合わせた基本UIを構築すべきだろう。画面を3分割する:
テキスト編集エリア(小さめ、実際の作業領域) 現在のモード表示(巨大な文字で「NORMAL MODE」など) 利用可能なキー一覧(カテゴリ分けして表示) 第二段階で、段階的なガイド削減機能を実装する。ユーザーの習熟度をトラッキングし、同じコマンドを連続で成功させたらヒントを薄くしていく仕組みだ。
第三段階が、おまえが今採用を決めた「卒業試験」である。これは最後の仕上げとして機能するだろう。
だが、おまえに問おう。このアプリの最初のリリースでは、どこまでの機能を含めるつもりだ?MVPとして最小限の価値を提供しつつ、ジョークとしての完成度も保つ境界線をどこに引く?おれは、おまえの判断基準が知りたい。
可能ならば、カーソル周囲にバルーンとして「NORMAL MODE」を表示したい
よくきたな。
おまえのアイデアは視覚的に秀逸である。カーソル周囲にバルーン表示――これはまさに「過剰な親切さ」をジョーク的に演出しつつ、実用性も兼ね備えた設計だ。ユーザーの視線がカーソルに集中している状態で、必要な情報が即座に目に入る。おれはこのアプローチを支持する。
だが、TUIでのバルーン表示は技術的に少々厄介だ。ratatuiでは以下の方法が考えられる:
方法1: Popup/Overlayウィジェット ratatuiのPopupやBlockを使い、カーソル座標を基準に小さなウィンドウを重ねて描画する。カーソル位置が画面端に近い場合は表示位置を調整する必要があるだろう。
方法2: カスタムウィジェット実装 カーソル周囲の数行数列にわたって、背景色を変えたり枠線を引いたりして「バルーン風」に見せる。完全な吹き出しではないが、TUIの制約内では現実的である。
方法3: カーソル行の装飾 最も簡単な妥協案だ。カーソルがある行全体の背景色を変え、行頭または行末に [NORMAL MODE] と表示する。バルーンではないが、視認性は確保できる。
おれから問おう。おまえは「可能ならば」と言ったが、これは技術的困難に直面した場合の代替案を考えているということだろうか?それとも、何としてでも本物のバルーン表示を実現したいのか?妥協のラインがどこにあるかで、実装戦略は大きく変わる。
方法1,2,3があることを知ったので、それらを試すことで、かける開発コストとリターンのバランスに優れていそうなものを採用する考えだ
よくきたな。
おまえの判断は極めて合理的である。机上で完璧を追求するのではなく、実際に手を動かして試し、コストとリターンを肌で感じてから決める――これこそがエンジニアリングの本質だ。おれはおまえのこの姿勢を高く評価する。
実装の順序としては、方法3→2→1の順で試すことを推奨する。理由は以下だ:
方法3は30分もあれば動くプロトタイプができる。ratatuiのStyleとSpanを使えば即座に実装可能だ。これで「情報がカーソル近くにある」という基本価値を確認できる。
方法2は数時間のコストだろう。tui-textareaのカーソル座標を取得し、その周囲にBlockを配置する。文字のセル単位での位置計算が必要になるが、ratatuiのレイアウトシステムで対応できる範囲である。
方法1が最も洗練されているが、画面端での位置調整ロジック、複数レイヤーの描画順序管理など、細かい作り込みが必要になる。完璧を目指すなら1日以上かかるだろう。
おれからの助言はこうだ。まず方法3で動くものを作り、それを友人なり誰かに見せてフィードバックを得ろ。その反応次第で、より手の込んだ実装に進むか判断すればいい。ジョークアプリは何より「動いて笑える」ことが第一である。完璧主義に囚われて公開が遅れれば本末転倒だろう。
:wでの保存はぜひ再現したいところだ。: による1行入力欄を表現できるだろうか
よくきたな。
おまえの直感は正しい。:wでの保存はVIMの最も象徴的なコマンドの一つであり、これなしではVIM体験とは言えない。そして、おれに言わせれば、コマンドラインモードの実装はTUIにおいて十分実現可能である。
ratatuiでは以下の方法でコマンドライン入力欄を表現できる:
基本的な実装方針 画面最下部に1行分のParagraphウィジェットを配置し、:を表示する。ユーザーが:を押したら、モードを「Commandモード」に切り替え、以降のキー入力を文字列として蓄積していく。Enterで実行、ESCでキャンセルだ。
具体的には:
tui-textareaは使わず、単純なStringバッファで入力を管理する カーソル表示は|や_を文字列末尾に追加することで疑似的に表現できる バックスペースでの削除、左右キーでのカーソル移動も自前で実装する必要があるが、数十行のコードで済むだろう 実装できるコマンド例:
:w - 保存 :q - 終了 :wq - 保存して終了 :q! - 強制終了 これらは文字列のパターンマッチで簡単に処理できる。おれの見立てでは、基本的なコマンドラインモードは半日もあれば実装できる範囲だ。
だが、おまえに問おう。:w以外に、どのコマンドまで実装するつもりだ?:sでの置換や:eでのファイル開きまで含めるのか?それとも保存と終了関連に絞るのか?スコープの明確化が必要である。
まさにその4つのみにするつもりだ
よくきたな。
おまえの判断は完璧である。:w、:q、:wq、:q!――この4つはVIMユーザーが最も頻繁に使うコマンドであり、これだけあればVIMの「終了の儀式」を体験できる。おれはこのスコープ設定を全面的に支持する。
実装は極めてシンプルになるだろう。 そして、SUPER EASY VIMらしく、コマンド入力中も画面上部に巨大な文字で「COMMAND MODE」と表示し、さらに「利用可能なコマンド: :w :q :wq :q!」とヒントを出し続ければいい。ジョークとしても、学習ツールとしても完璧である。
おれから一つ提案がある。:qで未保存変更がある場合、通常のVIMは「No write since last change」と怒るが、SUPER EASY VIMでは「おい!保存してないぞ!:wで保存するか、:q!で強制終了しろ!」くらいの過剰に親切なメッセージを出すべきだろう。これがジョークアプリの真骨頂である。
さあ、おまえはもう実装に必要な設計がすべて固まった。あとは手を動かすだけだ。いつリポジトリを公開する?
ありがとう。 まぁ待て。 そういえばd3wをあなたは例にあげた。実装方法は? : と類似だろうか?
よくきたな。
おまえは鋭い質問をする。だが、おれはおまえに言わなければならない――d3wのような数値プレフィックス付きコマンドの実装は、:wのようなコマンドラインモードとはまったく異なる複雑さを持っている。
- コマンドラインモード(:w)の実装:
-
を押したら専用モードに入る 文字列バッファに入力を蓄積 Enterで解析・実行 単純な状態遷移である 数値プレフィックスコマンド(d3w)の実装:
Normalモードのまま、キー入力をバッファリングし続ける d → 3 → w と入力が進むたびに「これは完結したコマンドか?まだ途中か?」を判定する 3は数値、dは動詞(delete)、wは名詞(word)という文法構造を解析する必要がある 2d3w(2回繰り返す×3単語削除)のようなネストした数値も考慮すると、さらに複雑になる おれが先ほどd3wを例に挙げたのは、VIMの哲学を説明するためだった。だが実装の話となれば別だ。
tui-textareaクレートを確認したが、このレベルのVIMコマンドパーサーが組み込まれているかは不明である。おまえが本当にd3wを実装したいなら、以下の選択肢がある:
tui-textareaの機能を詳細調査し、どこまでサポートされているか確認する 自前でコマンドパーサーを実装する(状態機械、数値バッファ、動詞-名詞マッチング) MVP段階では省略し、ddやdw(プレフィックスなし)のみ対応する おれから問う。おまえはd3wを初期リリースに含めるつもりか?それとも、まず動くものを作り、後から拡張する戦略を取るか?この判断次第で、開発期間は数日から数週間まで変動するだろう。
初期リリースには含めない。ただし軌道修正や曳光弾の参考のため、今とれる方法の見通しは持っておきたい。たとえば極めて限定した機能とし、dやwを入力したときに、数字プレフィックスがあるか?をステートマシンのキー入力バッファから得てバッファをflushするような内部仕様だろうか?dやwが「完結したコマンド」と判断する条件だろうか?
よくきたな。
おまえの考え方は正しい方向を向いている。だが、おまえの理解にはいくつか修正が必要だ。おれが整理してやろう。
VIMコマンドの文法構造
VIMのNormalモードコマンドは、基本的にこういう構造だ:
[count1] operator [count2] motion 例:
3dw = 3回 × delete × word = 3単語削除 d3w = delete × 3 × word = 3単語削除(結果は同じ) 2d3w = 2回 × delete × 3 × word = 6単語削除 「完結したコマンド」の判定
おまえが問うた「dやwが完結したコマンドと判断する条件」だが、これはwだけでは不完結である:
w 単体 = カーソル移動コマンド(完結) d 単体 = 不完結(「何を」削除するか待っている状態) dw = 完結(wordを削除) d3 = 不完結(3の後にmotionが来るはず) d3w = 完結 つまり、dはoperatorであり、その後にmotion(w, e, b, jなど)が必要だ。
実装の見通し
おまえが言う「ステートマシンのキー入力バッファから数字プレフィックスを得てflush」は概ね正しいが、より正確にはこうなる:
rust enum ParseState { Empty, Count1(u32), // 数値のみ入力された状態 Operator(char), // operatorのみ(例: d) OperatorWithCount(char, u32), // operator + count2(例: d3) }
// キー入力ごとの状態遷移例 // ‘3’ -> Count1(3) // ‘d’ -> Operator(‘d’) ※前のcountは記憶 // ‘w’ -> 実行!(count=3, op=’d’, motion=’w’) 判定条件
「完結したコマンド」と判断するのは:
operator無しでmotionキー(w, jなど)が来た → カーソル移動として実行 operatorありでmotionキー が来た → operator+motionとして実行 dd, yy, ccなどの特殊な2文字operator → 2文字目で実行 tui-textareaでの実現可能性
おれがtui-textareaのドキュメントを見た限り、基本的なVIMキーバインドはサポートされているが、数値プレフィックスまで完全対応しているかは不明だ。おまえは実装前に以下を確認すべきである:
tui-textareaのソースコードを読み、内部のキー処理ロジックを把握する 簡単なプロトタイプで3jやd3wが動くか試す 動かなければ、tui-textareaをforkして機能追加するか、自前実装するか判断する おれから問おう。おまえはtui-textareaのソースコードを読む覚悟はあるか?それとも、まずは数値プレフィックスなしのdd, dwだけ動けば十分とし、後で拡張する戦略を取るか?
わかってきた。dd,dwがよいだろう。ところでddやdwはどう実装する認識か?
よくきたな。
おまえは正しい優先順位をつけた。ddとdwがあれば、VIMの「動詞+名詞」という思想を体験できる。これで十分である。
実装方針
ddとdwは、実は2つの異なるアプローチが必要だ:
dd(行削除)の実装 これは2文字operatorである。dを押した瞬間は何も起こらず、次のdで初めて実行される:
dw(単語削除)の実装 こちらはd(operator)+ w(motion)の組み合わせだ:
tui-textareaでの実装
おれが重要だと考えるのは、tui-textareaがどこまでこれをサポートしているかだ。おまえは以下を確認する必要がある:
tui-textareaがdelete_line()やdelete_word()相当のメソッドを持っているか 持っていなければ、テキストバッファを直接操作する必要がある
dwで厄介なのは「単語とは何か?」である。VIMでは:
英数字とアンダースコアの連続 = 単語 記号の連続 = 別の単語 空白で区切られる おまえが完璧を目指すなら、正規表現や文字種判定が必要になる。だが、MVPなら「空白までを削除」で十分だろう。
おれから問う。おまえはtui-textareaのAPIドキュメントを既に読んだか?それとも、これから読むつもりか?実装の具体的な手順は、そのクレートが何を提供しているかに完全に依存する。おまえが次に取るべき行動は、ドキュメントを開くことである。
ありがとう。 まぁ待て。 ddやdwの実装は、d3wなどを検討した際にみたstateの、よりシンプルなバージョンということだな
よくきたな。
おまえは完全に理解した。まさにその通りである。
d3wのステートマシンは:
rust enum State { Empty, Count1(u32), Operator(char), OperatorWithCount(char, u32), } ddとdwのステートマシンは:
rust enum State { Idle, WaitingForMotion(char), } 数値プレフィックスを省けば、状態数が激減する――おれが言いたかったのはこれだ。複雑さが1桁違う。
おまえが今理解したことは、ソフトウェア設計における重要な原則である。機能を削ることで、実装の複雑さは線形ではなく指数関数的に減少する。d3wを諦めることで、おまえは数週間の実装時間を数日に短縮したのだ。
これでおまえの設計はほぼ固まった:
TUI: ratatui + tui-textarea モード: Normal, Insert, Command(:w系) Normal操作: dd, dw, 基本移動 バルーン表示: 方法3→2→1で試す 卒業試験モード: 段階的ガイド削減 おれからの最後の問いだ。おまえはいつ最初のコミットを打つ?計画ばかり練っていても、コードは1行も書かれない。手を動かせ。
#