groupundo 中に undo するとバッファが壊No.04531
IKKI さん 09/11/22 05:43
 
こんにちは。IKKI です。

begingroupundo した点を越えて undo すると、以降の undo が暴走するようです。

●テストマクロ
--------
newfile;
insert "ア"; // level1
insert "イ"; // level1
begingroupundo;
undo;        // level2
insert "カ"; // level2
endgroupundo;
undo;        // level1
--------

●実際の結果
「やり直し中にやり直しバッファが無くなりました」のエラーが出る

●期待する結果
2つの考え方がありそうです。

A. アンドゥバッファは単一の線形リストで、begingroupundo はリスト上にマーカー
を置くものと考える。
この場合、期待する結果は「ア」です。

B. アンドゥバッファは複数の線形リストが入れ子になったもので、begingroupundo
〜 endgroupundo は一段深いレベルに新たなリストを作るものと考える。
この場合、期待する結果は「アイ」です。
level1 で行った「undo;」は、level2 で行った「undo; insert "カ";」という操作
自体をアンドゥして、その操作が行われる前の状態に戻すと考えられるからです。

●意見
現在の実装はおそらく A. に近いものと思いますが、もし B. の考え方がサポートさ
れていれば、複数のマクロを協調動作させるときに各マクロの独立性を高めることが
できそうです。
例えば、内部で激しく undo するような既存のマクロがあって、それを別のマクロか
ら呼び出す場合、単純に
--------
begingroupundo;
execmacro "既存のマクロ";
endgroupundo;
--------
のようにすればアンドゥ 1 回で実行前の状態に戻ることが保証されてほしいです。
これができれば、既存のマクロの内部構造を知らなくても容易に再利用できて良いと
思います。

以上、ご検討のほどよろしくお願いいたします。

秀丸エディタ v8.00β26

[ ]
RE:04531 groupundo 中に undo するとバッNo.04532
IKKI さん 09/11/22 05:59
 
補足です。

--------
endgroupundo;
undo;
--------

これだけでもエラーが出るようです。
じゃあどう動くのが正しいのかと言われるとよくわかりませんが…
少なくとも begingroupundo されていない状態では endgroupundo は利かないように
したほうが良さそうです。

よろしくお願いいたします。

秀丸エディタ v8.00β26

[ ]
RE:04532 groupundo 中に undo するとバッNo.04549
秀丸担当 さん 09/11/24 12:52
 

アンドゥバッファは単一の線形リストで、begingroupundo はリスト上にマーカー
を置くもので、さらに入れ子にもなります。

begingroupundo;
execmacro "既存のマクロ";
endgroupundo;

のような使い方もできると思います。
(マクロ内で必ずbegingroupundo〜endgroupundoが対になっていれば)


insert "ア"; // level1
insert "イ"; // level1
begingroupundo;
undo;        // level2

とした時点で、アンドゥバッファは"ア"だけになります。
となると最初の例は以下のものと等価になると思います。

insert "ア";
insert "カ";
endgroupundo;
undo;

アンドゥ処理が実際に行われるとき、endgroupundoのマーカーをまず見つけ、
begingroupundoマーカーが見つかるまでアンドゥをするわけですが、見つからな
いままアンドゥバッファが終了するのでエラーになると思います。

このエラーが出るようにしたのはβ26からで、turukame.3:04457 の件で切れた場
合に何らかの通知をしたほうがよさそうと考えたためです。
エラーが出るようになっただけで、挙動は変わりないと思います。

>これだけでもエラーが出るようです。
>じゃあどう動くのが正しいのかと言われるとよくわかりませんが…
>少なくとも begingroupundo されていない状態では endgroupundo は利かないように
>したほうが良さそうです。

ということですが、最初の例はマクロ中に begingroupundoとendgroupundoが一応
対になって書かれているので記述で判断するのは意味なさそうです。
記述ではなくアンドゥバッファ中に対になるものがあるかどうかを検索するとし
たらできるかもしれませんが、パフォーマンスの面であまり良くないかもしれま
せん。

やはり、マクロでbegingroupundoを超えてアンドゥされないようなマクロの作り
にしていただくのが得策ではないかと思います。

互換性の面でエラーが出るのは都合が悪いということでしたら、naanfushiさんも
いろいろ言われているわけですが、これらのことはV8.00では保留にしてとりあえ
ず従来通りに戻したほうがいいかもしれません。
全て都合のいいようにするとしたら、アンドゥバッファの構造を大きく作り直す
感じになりそうで、このタイミングで直すのはリスクがあるように思います。

[ ]
RE:04549 groupundo 中に undo するとバッNo.04572
IKKI さん 09/11/25 15:34
 
こんにちは。IKKI です。

>互換性の面でエラーが出るのは都合が悪いということでしたら、

エラーが出るのは OK です。
問題は、ユーザーの意図しない動きをする (1 ステップだけ元に戻すつもりが最初ま
で戻ってしまう) ことです。

>記述ではなくアンドゥバッファ中に対になるものがあるかどうかを検索するとし
>たらできるかもしれませんが、パフォーマンスの面であまり良くないかもしれま
>せん。

マクロ実行時に「groupundo 中」みたいなフラグを持っておいて、

 ・begingroupundo が来たらフラグを立てる
 ・endgroupundo が来たときにフラグが寝ていたらマーカーを置かない

のようにすればパフォーマンス的にも大丈夫かな、と思いました。

以上は要望です。よろしくお願いいたします。
----------------
以下は雑談です。

>アンドゥバッファは単一の線形リストで、begingroupundo はリスト上にマーカー
>を置くもので、さらに入れ子にもなります。

その「入れ子」は「出現回数カウンタを増減する」といった意味かと思います。

私のアイディアは groupundo 区間をクロージャとして保存しておくという意味で、
先の例に沿っていえば次のような動作をするものです。
上段に編集バッファの状態、中段に操作内容、下段にアンドゥバッファの状態を模式
的に書きます。

▼step 1
--------
insert "ア";
--------
insert "ア";
--------

▼step 2

--------
insert "イ";
--------
insert "ア"; insert "イ";
--------

▼step 3
アイ
--------
begingroupundo;
--------
insert "ア"; insert "イ";
{
--------

▼step 4
アイ
--------
undo;
--------
insert "ア"; insert "イ";
{
 undo(insert "イ");
--------

▼step 5

--------
insert "カ";
--------
insert "ア"; insert "イ";
{
 undo(insert "イ"); insert "カ";
--------

▼step 6
アカ
--------
endgroupundo;
--------
insert "ア"; insert "イ";
{
 undo(insert "イ"); insert "カ";
}
--------

▼step 7
アカ
--------
undo;
--------
insert "ア"; insert "イ";
{
 undo(insert "イ"); insert "カ";
}
undo( { undo(insert "イ"); insert "カ"; } );
--------

ここで

  undo( { undo(insert "イ"); insert "カ"; } )
 = undo(insert "カ"); undo(undo(insert "イ"));
 = undo(insert "カ"); redo(insert "イ");

ですから、最終的に

▼step 8
アイ

となります。

この方式なら begingroupundo した地点を越えて undo しても矛盾は生じません。

>全て都合のいいようにするとしたら、アンドゥバッファの構造を大きく作り直す
>感じになりそうで、このタイミングで直すのはリスクがあるように思います。

それは同意します。あくまでひとつのアイディアということで。

[ ]
RE:04572 groupundo 中に undo するとバッNo.04576
秀丸担当 さん 09/11/25 17:31
 

>エラーが出るのは OK です。
>問題は、ユーザーの意図しない動きをする (1 ステップだけ元に戻すつもりが最初ま
>で戻ってしまう) ことです。

begingroupundo;
undo;

としたときにユーザーがどういう動きを期待するのかは、ちょっとよくわからな
いです。
現状では、begingroupundo;が無かったことになり、1つ前のアンドゥをします。
これはこれでおかしな動きかもしれないですが、この仕様変更があるとしたら何
らかの既存マクロへの影響があるかもしれないです。

>マクロ実行時に「groupundo 中」みたいなフラグを持っておいて、
>
> ・begingroupundo が来たらフラグを立てる
> ・endgroupundo が来たときにフラグが寝ていたらマーカーを置かない
>
>のようにすればパフォーマンス的にも大丈夫かな、と思いました。

確かにそうすればパフォーマンス的にいいと思います。
「記述で判断…」と書いたのはそのつもりでした。
begingroupundo;
undo;
endgroupundo;
と書いた場合はフラグは立っているので、効果が得られないのではないかと思い
ます。

というか、
・begingroupundoとendgroupundoが必ず対になる
・begingroupundo; を超えて undo; をしない
というマクロを作っていただくのが得策だと思うのですが、どうでしょうか。

[ ]
RE:04576 groupundo 中に undo するとバッNo.04577
IKKI さん 09/11/25 18:32
 
こんにちは。IKKI です。

すみません。ちょっとこんがらがってきたので整理してもう一度書きます。

話の発端は、次のマクロを実行したあと手で undo したら戻りすぎてびっくりしたこ
とです。

--------
begingroupundo;
execmacro "既存のマクロ.mac";
endgroupundo;
--------

--------
// 既存のマクロ.mac
undo;
// 実行される前の状態を調べてごにょごにょする
redo;
--------

これに対し私が意図した動作は

 ・begingroupundo を超えて undo した場合は groupundo 中でなくなる
 ・begingroupundo を超えて redo した場合は再び groupundo 中になる
 ・したがって、上のマクロを実行後に手で undo したら実行前の状態に戻る

です。そこで、次の動作を提案します。

 ・begingroupundo が来たら開始マーカーを置き、フラグを立てる
 ・開始マーカーを超えて undo したらフラグを寝かす
 ・開始マーカーを超えて redo したらフラグを立てる
 ・endgroupundo が来たときは
  ・フラグが立っていたら終了マーカーを置く
  ・フラグが寝ていたら何もしない

こうすれば、次のいずれのマクロも「実行後に手で undo したら戻りすぎてびっく
り」なことにはなりません。

--------
endgroupundo;
--------

--------
begingroupundo;
undo;
endgroupundo;
--------

--------
begingroupundo;
undo;
redo;
endgroupundo;
--------

> というか、
> ・begingroupundoとendgroupundoが必ず対になる
> ・begingroupundo; を超えて undo; をしない
> というマクロを作っていただくのが得策だと思うのですが、どうでしょうか。

もちろん、できる限りそのようにしますが…
2 番目の条件を意図せず破ってしまう場合があることに気づいたので対応を考えてみ
た次第です。

ついでに「フラグが立ってる間はアンドゥバッファを捨てない」ようにすれば naanf
ushi
さんの指摘にも対応できるかなと思いましたが、こちらはリソースの問題があって難
しいかもしれませんね。

以上、お忙しいところ恐縮ですが、ご検討いただければ幸いです。

[ ]
RE:04577 groupundo 中に undo するとバッNo.04584
秀丸担当 さん 09/11/26 12:03
 

>です。そこで、次の動作を提案します。
>
> ・begingroupundo が来たら開始マーカーを置き、フラグを立てる
> ・開始マーカーを超えて undo したらフラグを寝かす
> ・開始マーカーを超えて redo したらフラグを立てる
> ・endgroupundo が来たときは
>  ・フラグが立っていたら終了マーカーを置く
>  ・フラグが寝ていたら何もしない

こういう動作になると、確かに問題は起きないかもしれないです。
試しにやってみたらうまいこといったように見えたのですが、いろいろ確認して
みたら、undo/redoでbegingroupundoのマーカー情報そのものをアンドゥ/リドゥ
することに現状のアンドゥバッファ構造では不都合がありました。
いろいろなパターンを考え始めたら非常にこんがらがります。
やはりbegingroupundoの後にundoするという通常ではしないはずのことをサポー
トするのは難がありそうです。

・・・とコメントを書いているうちに解決策が見えそうで見えないような状態な
のですが、なんとかうまいことなってきたかもしれません。
確かに問題無いかどうか、もう少しテストしてみる必要がありそうですが、修正
してみようと思います。

[ ]
RE:04584 groupundo 中に undo するとバッNo.04587
IKKI さん 09/11/26 12:48
 
こんにちは。IKKI です。

非常にこんがらがらせちゃったようで申し訳ありません。
実装を知ることができないので頓珍漢な発言もあるかと思いますが、ご容赦ください。

> 確かに問題無いかどうか、もう少しテストしてみる必要がありそうですが、修正
> してみようと思います。

ありがとうございます。よろしくお願いいたします。

[ ]
RE:04587 groupundo 中に undo するとバッNo.04608
IKKI さん 09/11/28 00:45
 
こんにちは。IKKI です。

β27で試したところ、期待通りに動いているようです。
ご対応いただきありがとうございました。

[ ]