@nqounetです。
シリーズ「Mooで作る簡易テキストエディタ」の第7回です。
前回の振り返り
前回は、履歴管理を専門に行うHistoryクラスを作成しました。
Historyクラスには2つのスタックがあります。
undo_stack— 実行したコマンドを積み上げるredo_stack— Undoしたコマンドを積み上げる(Redo用)
そして、execute_commandとundoメソッドを実装しました。
| |
undoメソッドでは、コマンドをundo_stackから取り出し、redo_stackに移動しています。これは、後でRedoできるようにするためです。
今回は、いよいよredoメソッドを実装します。
Redo機能とは
Redo機能は、Undoした操作をやり直す機能です。
たとえば、以下のシナリオを考えてみましょう。
- 「Hello」を入力
- Undo(「Hello」を取り消し)
- 「やっぱり『Hello』が必要だった!」
ステップ3で、Undoした操作をやり直したくなることがあります。これがRedo機能です。
多くのエディタでは、Ctrl+ZでUndo、Ctrl+YまたはCtrl+Shift+ZでRedoができます。
スタック間のコマンド移動を理解する
Undo/Redoの動作を図で理解しましょう。
flowchart LR
subgraph initial["初期状態"]
direction TB
subgraph us1["undo_stack"]
U1["cmd1"]
U2["cmd2"]
U3["cmd3 ←top"]
end
subgraph rs1["redo_stack"]
R0["(空)"]
end
end
subgraph after_undo["Undo後"]
direction TB
subgraph us2["undo_stack"]
UA1["cmd1"]
UA2["cmd2 ←top"]
end
subgraph rs2["redo_stack"]
RA1["cmd3 ←top"]
end
end
subgraph after_redo["Redo後"]
direction TB
subgraph us3["undo_stack"]
UB1["cmd1"]
UB2["cmd2"]
UB3["cmd3 ←top"]
end
subgraph rs3["redo_stack"]
RB0["(空)"]
end
end
initial -->|"undo()"| after_undo
after_undo -->|"redo()"| after_redo
style us1 fill:#e1f5fe
style us2 fill:#e1f5fe
style us3 fill:#e1f5fe
style rs1 fill:#fff3e0
style rs2 fill:#fff3e0
style rs3 fill:#fff3e0
コマンドの移動をまとめると、以下のようになります。
| 操作 | undo_stack | redo_stack |
|---|---|---|
| execute_command | push | クリア |
| undo | pop → | → push |
| redo | push ← | ← pop |
undoとredoは、対称的な操作です。
undo—undo_stackからpop → コマンドのundoを呼ぶ →redo_stackにpushredo—redo_stackからpop → コマンドのexecuteを呼ぶ →undo_stackにpush
redoメソッドを実装する
では、redoメソッドを実装しましょう。
| |
redoメソッドは、以下の処理を行います。
redo_stackが空なら、何もせずにreturnするredo_stackから最後のコマンドをpopする- コマンドの
executeメソッドを呼び出す(やり直す) - 実行したコマンドを
undo_stackにpushする(再度Undo可能にする)
undoメソッドと比較してみましょう。
| メソッド | popするスタック | 呼び出すメソッド | pushするスタック |
|---|---|---|---|
| undo | undo_stack | $command->undo | redo_stack |
| redo | redo_stack | $command->execute | undo_stack |
対称的な構造になっていることがわかります。
Undo/Redoの連続操作をデモする
では、redoメソッドを使って、Undo/Redoの連続操作をデモしましょう。
| |
実行結果は以下のようになります。
| |
Undo/Redoを何度でも繰り返せることが確認できました。
新規操作でredo_stackがクリアされることを確認する
前回説明したように、新しい操作を実行するとredo_stackがクリアされます。これは多くのエディタの動作と同じです。
| |
実行結果は以下のようになります。
| |
Undoした後に新しい操作を実行すると、Redo履歴がクリアされることが確認できました。
今回作成した完成コード
以下が今回作成した完成コードです。Historyクラスにredoメソッドを追加し、Undo/Redoの連続操作を可能にしました。
| |
実行結果は以下のようになります。
| |
まとめ
redoメソッドはundoメソッドの対称的な操作であるredoはredo_stackからpop → コマンドのexecuteを呼ぶ →undo_stackにpush- Undo/Redoは何度でも繰り返せる
- 新規操作を実行すると
redo_stackはクリアされる
次回予告
Undo/Redo機能が完成しました。これで操作の取り消しとやり直しが自由にできるようになりました。
しかし、「複数の操作をまとめて実行・Undoしたい」場面があります。たとえば、「検索と置換」機能では、複数の置換操作を1つのUndoで元に戻したいことがあります。
| |
次回は、複数の操作を1つにまとめるマクロ機能(MacroCommand)を実装します。
お楽しみに。
