TextAreaのundoの実装1

TextAreaにundo機能を実装してみたけれど、結構量ありそうなので分けて書くことにする。というわけで今回は具体的なコードに入る前の準備。

TextAreaに限らず、テキストを扱うmxコンポーネントではcut/copy/pasteはあるがなぜかundoはない、という単純な背景。結構メジャーな問題なのでライブラリとかあるんじゃないかなーとコーディングしながら思ったらやっぱりあった。

このうち1番上はリンク切れ、2番目は別によくわからないフレームワークが必要で、3番目は内部構造が下記に書く一番単純な構造をしているのでどれも採用には厳しいかなと思った。

ここで要件を書くと、

  • TextAreaでCtrl+Zを押すと、直前の操作前の状態に戻す。1回ではなく、基本的に何回も戻れるようにする。
  • 操作は、n文字を一度に入力, backspaceの押下, deleteの押下, cut/copy/pasteのどれか。(replaceは含まないでおく)
  • できればTextAreaだけでなく他のテキスト入力系コントロール(TextInput, ComboBox)にも使えるようにしたい。
  • できればRedoも実装したい。

となる。何回でも戻れるようにするので、内部に何らかの形で履歴を保管しておかなければならない。

まず最初に考えつく方法が、各操作前のテキストをそのまま履歴に保存する方法。これなら操作の種類に関係なくundoでき、構造も単純だが、テキストの長さをL, 履歴の長さをTとすると、O(LT)の記憶領域を必要とする。Twitterクライアント用途だとLはよほどひねくれた編集をしない限りは140以下になり、別にこれでもかまわない。だがこれはエレガントではない、そう僕の勘が告げている。

次に考えつく方法が、各操作前と後のテキストの差分をとって履歴に保存する方法。これなら範囲選択した状態でペースト、とか無茶なことをしない限りは上記の方法よりは記憶領域が少ない。今回は要件に入っていないが、replaceされると結構困る。

他には、操作自体を履歴に保存する方法がある。上述の2つは操作の前提と結果しか見てないのに対し、こちらのほうが確実な感があり、さらに省メモリである。だが、replaceなどでちょっと時間のかかる操作の場合毎回実行しないといけないのでこちらのほうが分が悪い。テキストエディタなんかはこの方式なんだろうか。

下に行くほど、操作の種類への依存度が上がっていく。
ここでは2番目の方法で実装してみたい。結論から言うと、2番目の方法はundoが主目的でない場合にはあまりおすすめできない。