selectcolormarkerのバグ?No.08530
IKKI さん 15/02/04 22:21
 
秀丸担当さん、こんにちは。
selectcolormarkerの挙動について2点、ご確認願います。

// テスト.mac
colormarker 0xFFFFFF, 0x00CCFF, 11, 2, 0, "layer0";
escape;
gofiletop;
selectcolormarker "layer1";
message str(selecting);

1. 存在しないレイヤーを指定すると制御が返ってこない
適当な文字列を選択して上記テストマクロを実行すると、メッセージが表示されませ
ん。
この状態で他のマクロを実行しようとすると「他の秀丸エディタがマクロ実行中で
す」と言われます。
内部で無限ループしてるような感じです。

2. ゼロ幅のカラーマーカーを見逃す場合がある
こちらは再現率100%ではないのですが…
 (1) 上記テストマクロの2か所のレイヤー名を同じにする (ex. "layer1" を "laye
r0" に変える)
 (2) ゼロ幅の範囲選択をする (ex. [Shift+←] [Shift+→])
 (3) マクロ実行
という手順で、カラーマーカーが塗られているのに選択されていない状態になる場合
があるようです。

ご確認のほどよろしくお願いいたします。

秀丸エディタ v8.50β1

[ ]
RE:08530 selectcolormarkerのバグ?No.08531
IKKI さん 15/02/04 22:27
 
>2. ゼロ幅のカラーマーカーを見逃す場合がある
手元で再現率100%になる手順が見つかりました。

// テスト.mac
colormarker 0xFFFFFF, 0x00CCFF, 11, 2, 0, "layer0";
escape;
gofiletop;
selectcolormarker "layer0";
message str(selecting);

(1) 範囲選択していない状態でマクロ実行
 → カーソルが文頭へ飛び、メッセージ「0」が表示される (OK)
(2) 適当な場所でゼロ幅の範囲選択をする
(3) マクロ実行
 → カーソルが文頭へ飛び、メッセージ「0」が表示される (NG)

手順(3) のあと、ゼロ幅の範囲が選択され、メッセージ「1」が表示されることを期
待しています。
そちらで再現しますでしょうか。

[ ]
RE:08530 ゼロ幅のカラーマーカーNo.08532
IKKI さん 15/02/04 22:46
 
ついでにおたずねします。

____→←____

上記テキストで「→」と「←」の間にゼロ幅のカラーマーカーを塗ったとします。
その後「→」や「←」を削除すると、カラーマーカーも消えてしまいます。
カラーマーカーの種類 (維持/分裂/消去) に関わらず同じ挙動です。

Q1. 実装上、ゼロ幅のカラーマーカーの情報は誰が持っているのでしょうか?
左隣の文字や右隣の文字にくっついているのでしょうか。

Q2. テキスト中にゼロ幅の目印を置いたあと、それに隣接する文字を削除しても目印
が残るような、何かうまい方法はないでしょうか?
目印はカラーマーカーでなくてもいいですが、テキスト中に制御文字を入れちゃうと
か、座標を変数に持っておくとかいう手はナシで。

ゼロ幅の目印がまともに使えるようになると、複数選択がらみのマクロを書くのがだ
いぶ楽になりそうです。

[ ]
RE:08531 selectcolormarkerのバグ?No.08533
秀丸担当 さん 15/02/05 10:13
 

バグ情報ありがとうございます。
1. 2. の件両方とも再現させることができました。
1.のほうはまずいバグでした。
V8.51で早いうちに修正させていただきます。

[ ]
RE:08532 ゼロ幅のカラーマーカーNo.08534
秀丸担当 さん 15/02/05 13:48
 

ゼロ幅のカラーマーカーは、実装上は文字にひっついているというわけではなく、
座標で覚えているということになります。
隣接する文字を削除しても維持するようには、確かになっていないですね。
やるとしたら、colormarker文の種類のパラメータの0動作を変更するのは互換上
よくないと思うので、3も指定できるようにして、ゼロ幅でも維持できるように
するということが考えられると思います。
他の方法でなんとかするとしたら、カラーマーカーを隣り合わせに2つ用意して
どっちかが消えたら片方を復活させるとかすればできそうな気もしますが、非常
にややこしいことになると思います。

[ ]
RE:08534 ゼロ幅のカラーマーカーNo.08536
IKKI さん 15/02/06 01:04
 
ご回答ありがとうございます。
以下は「もし何かしら対応するなら…」という仮定の話です。

>隣接する文字を削除しても維持するようには、確かになっていないですね。
>やるとしたら、colormarker文の種類のパラメータの0動作を変更するのは互換上
>よくないと思うので、3も指定できるようにして、ゼロ幅でも維持できるように
>するということが考えられると思います。
ん? 隣接文字削除時に維持するかどうかと、編集時の維持/分裂/消去は独立な話
なので、組合せで6通りの指定が必要になるのでは…。
気になったので調べてみましたが、現状でも幅1文字以上のカラーマーカーは隣接文
字を削除しても影響を受けないです。
https://dl.dropboxusercontent.com/u/861457/20150206-002821.png
なので、むしろ幅ゼロのカラーマーカーの動作が不整合だったということで、バグと
して直してしまってもいいような気がします。

これを調べてて気づいたのですが、カラーマーカーの左隣に文字を挿入したときの動
作がちょっと不思議というか、右隣に挿入したときの動作と対称ではないんですね。
整合性という意味では、上のスクリーンショットの右側のような動作にするべきだっ
たのではないかと、いまさらながら思います。

>ゼロ幅のカラーマーカーは、実装上は文字にひっついているというわけではなく、
>座標で覚えているということになります。
あ、そうなんですね。
ちなみに、ゼロ幅でないカラーマーカーも同様に、テキストとは別に座標を持ってい
るのでしょうか。

>他の方法でなんとかするとしたら、カラーマーカーを隣り合わせに2つ用意して
>どっちかが消えたら片方を復活させるとかすればできそうな気もしますが、非常
>にややこしいことになると思います。
なるほど。それをするぐらいなら、一時的に制御文字を挿入しておいて最後に削除し
た方がなんぼかマシですね…。

とりあえず、今作ってるマクロに関してはゼロ幅のカラーマーカーを使わない方向で
もうちょっと考えてみます。
秀丸担当さんの方でも、このへんの仕様をうまいこと整理できないかどうか検討して
みていただけると嬉しいです。

[ ]
RE:08536 ゼロ幅のカラーマーカーNo.08537
秀丸担当 さん 15/02/06 12:25
 

幅のあるカラーマーカーの場合も内部的には座標で覚えていますが、実装方法は
あまり問題ではなく、ユーザーが扱う表面的には、幅がある場合は文字に色が付
いていて、色は文字が所有していると考えていいと思います。

ワードパッドなど、一般的なワープロ系のソフトの挙動も考えると、幅がある場
合は、検証していただいた画像の
「幅あり」+「編集後(現状)」
が、一般的な挙動とも合っている状態だと思います。

幅が無い場合は、文字が所有しているとは言えないので、どうにかできたらいい
と思います。

現状で消えている理由としては、DeleteキーやBackSpaceキーではなく、Shift+
矢印キーで一文字だけ範囲選択して削除したものを考えると、わかりやすいです。
範囲の中に、カラーマーカーの先頭から末尾までが含まれているから消えている
ということになります。

何かワープロ系ソフト以外に事例が無いか考えてみたのですが、ブラウザの
contenteditableによる編集が近いようです。

<div contenteditable="true">
1<span style="background-color:red;padding:1">234</span>5<BR>
A<span style="background-color:red;padding:1"></span>B<BR>
</div>

IEとchromeの場合は秀丸エディタとだいたい同じようです。
Firefoxの場合は、右からカーソル移動と左からカーソル移動で違ったり、行頭
に2回カーソルが止まったりして、ありえない位置に移動できるので、テキスト
エディタと同じ解釈はできなさそうで、微妙でした。

ブラウザの挙動も踏まえると、仕様としては現状の挙動はありとして、何かやる
としたら別のモードを設けるといったほうがよさそうです。

[ ]
RE:08537 ゼロ幅のカラーマーカーNo.08538
IKKI さん 15/02/08 06:40
 
諸々ご説明ありがとうございます。
「仕様として現状の挙動はあり」ということで了解しました。
とりあえず、今作っているマクロはゼロ幅のカラーマーカーを使わないで実装できま
した。
以下は今すぐ解決を要する話ではありません。

残る懸念として、今後すべてのマクロを複数選択に対応させていくにあたり、
マクロで複数選択を扱うにはカラーマーカーに頼るしかないわけですが、
「範囲がゼロ幅の場合は隣接文字を削除してはならない」となると、つまり
各範囲がゼロ幅か否かで場合分けしなければならなくなって、これは正直キツい、
非常に見つけにくいバグの温床になりそう…という点があります。

カラーマーカーの種類として「隣接文字を削除しても消えない」を増やすのが良い解
決方法なのかどうかわかりませんが、
将来的に、マクロで複数選択を扱うのに場合分けを要しないような方法ができるとい
いと思います。

[ ]
RE:08538 ゼロ幅のカラーマーカーNo.08539
秀丸担当 さん 15/02/09 16:45
 

隣接文字を削除しても消えない動作を作ることはできると思います。
V8.52以降では、機能追加や変更も加えながらバージョンアップしていこうと考
えています。
あまり早急に作りすぎて後々面倒な仕様になると困ることになるかもしれないの
で、急ぐ話でなければ、慎重に考えたいと思います。
他に何かひっかかることがあれば知らせていただければ考えます。

[ ]
RE:08539 置換で foundtopx 等を更新No.08540
IKKI さん 15/02/11 17:15
 
>あまり早急に作りすぎて後々面倒な仕様になると困ることになるかもしれない
その通りだと思います。慎重にお願いします。

>他に何かひっかかることがあれば知らせていただければ考えます。
ということなので、別件ですがこのスレッドに繋げてしまいます。

置換で foundtopx 等が更新されないのが微妙に不便です。
マクロで置換後の文字列を選択状態にする処理を書くことがしばしばあります。
ヒットした先頭位置にカーソルを動かすとき、置換後の文字列が固定長なら column
- strlen() で済むのですが、後方参照を含んでいて長さが不定だったりすると…

// 対処1.mac
replacedown "「(.*?)」", "『\\1』", regular;
if (result) {
 ##endx = x;
 ##endy = y;
 undo;
 ##topx = x;
 ##topy = y;
 redo;
 beginsel;
 moveto ##topx, ##topy;
 endsel;
}

// 対処2.mac
$src = "「(.*?)」";
$rpl = "『\\1』";
searchdown $src, regular;
if (result) {
 ##topx = foundtopx;
 ##topy = foundtopy;
 $$text = gettext(foundtopx, foundtopy, foundendx, foundendy, true);
 $$text = dllfuncstr("ReplaceRegular", "\\r", $$text, 0, "", 2);
 $$text = dllfuncstr("ReplaceRegular", $src, $$text, 0, $rpl, 2);
 insert $$text;
 beginsel;
 moveto ##topx, ##topy;
 endsel;
}

…のような、微妙に無駄の多い処理にせざるをえません。
対処2.mac は searchdown のヒットと ReplaceRegular のヒットが厳密に一致すると
は限らない (例: 検索パターンが ^ や $ を含んでいる場合) という問題もあります。

この無駄をなくす方法は非常に簡単で、置換が foundtopx 等を更新してくれれば解
決する話です。
hidesoft.2:25506 で「仕様です」と明言されていますが、将来的に何とかなりませ
んでしょうか。

ゆっくりご検討いただければ幸いです。

[ ]
RE:08540 置換で foundtopx 等を更新No.08541
秀丸担当 さん 15/02/12 14:06
 

確かにreplacedownではfoundtopx等は書き換えていないです。
いまさらながら、簡単に取得できる方法があってもいいと思います。
replacedownでfoundtopxを書き換えるとしたら、互換性が気になります。
現状で簡単に対策するとしたら、searchdownしてから、直後に同じ検索文字列で
replacedownすると簡単だと思います。
そういう使い方をされている方がいるとすると、replacedownではfoundtopxが書
き換わらないという前提があるから成り立つことになると思います。(この例だ
けでは結果的には同じですが)
そういったことを考えると、やるとしたらreplacedtopxのような別のキーワード
があったらいいと思いますが、どうでしょうか。

[ ]
RE:08541 置換で foundtopx 等を更新No.08543
IKKI さん 15/02/15 22:12
 
コメントありがとうございます。

>現状で簡単に対策するとしたら、searchdownしてから、直後に同じ検索文字列で
>replacedownすると簡単だと思います。
なるほど、灯台下暗しでした。当座その作戦で行きます。
# 計算量的に無駄なことには変わりなく、対象数が多いと速度の問題があるかもしれ
ませんが、今困っているわけではないです。

>replacedownでfoundtopxを書き換えるとしたら、互換性が気になります。
>やるとしたらreplacedtopxのような別のキーワードがあったらいいと思いますが、
>どうでしょうか。
うーん…、それだと replacedendx が検索ヒット文字列の終端なのか置換後文字列の
終端なのか紛らわしいような気もします。
# 置換後文字列の終端はカーソル位置なのでキーワード不要 ⇒ 背理法的に考えれば
前者とわかる…?
かといって、この程度の問題のために setcompatiblemode のビットを使うのももっ
たいないですかね。
それ以外の代案は、今すぐには思いつかないです。思いついたらまた書き込みます。

[ ]
RE:08539 複数選択で delete/backspace でNo.08544
IKKI さん 15/02/15 22:55
 

また別件っぽいですが、このスレッドにつなげてしまいます。

まず前提として、複数選択状態で Backspace や Delete を押した場合の動作は
 if (すべての範囲が幅ゼロである) {
  範囲外の文字を削除する; //  Backspace は左隣、Delete は右隣
 } else { // 幅1文字以上の範囲がひとつでも存在する
  範囲内の文字を削除する; // 幅ゼロの箇所は影響を受けない
 }
という仕様になっていると理解していますが、正しいでしょうか。

// test.mac
clearreservedmultiselall;
call ReplaceAllSelect "(?\\1)[!-~](●)[!-~]", " ", 0x10;
call ReplaceAllSelect "●", "", 0x10;
selectreservedmultisel;
endmacro;
ReplaceAllSelect:
 gofiletop;
 replacedown $$1, $$2, ##3;
 while (result) {
  ##rdx = x;
  ##rdy = y;
  beginsel;
  moveto2 column - strlen($$2), lineno;
  endsel;
  reservemultisel;
  escape;
  moveto ##rdx, ##rdy;
  finddown2;
 }
 return;

さて、次のサンプルテキスト上で test.mac を実行し、続いて Backspace/Delete を
2回押すとします。

https://dl.dropboxusercontent.com/u/861457/20150215-0.png (サンプルテキスト)
https://dl.dropboxusercontent.com/u/861457/20150215-1.png (マクロ実行後)

この状態で Backspace/Delete を押したら、こうなることを期待します。

https://dl.dropboxusercontent.com/u/861457/20150215-ok2.png (Backspace/Delet
e 1回目)
https://dl.dropboxusercontent.com/u/861457/20150215-ok3b.png (Backspace 2回目)
https://dl.dropboxusercontent.com/u/861457/20150215-ok3d.png (Delete 2回目)

しかし、実際にはこうなります。

https://dl.dropboxusercontent.com/u/861457/20150215-ng2b.png (Backspace 1回目)
https://dl.dropboxusercontent.com/u/861457/20150215-ng3b.png (Backspace 2回目)
https://dl.dropboxusercontent.com/u/861457/20150215-ng2d.png (Delete 1回目)
https://dl.dropboxusercontent.com/u/861457/20150215-ng3d.png (Delete 2回目)

この挙動はバグだと思うのですが、いかがでしょうか。
ご確認のほどよろしくお願いいたします。

[ ]
RE:08543 置換で foundtopx 等を更新No.08545
秀丸担当 さん 15/02/16 12:29
 

replacedendxがあるとしたら、置換後位置という意味として、カーソル位置と同
じ場所と思いましたが、カーソル位置と同じなのでそもそも必要ではない気がし
ます。
検索にヒットした文字列の終端を示すにしても、既にその場所は無くなっている
ので、検索にヒットした文字列も正確に得る必要があるとしたら、やはり
searchdownしてからヒットした文字列を得て、その後置換をしたほうがいいと思
います。
急ぐことでなければ、これも慎重に考えたいと思います。

[ ]
RE:08544 複数選択で delete/backspace でNo.08546
秀丸担当 さん 15/02/16 12:30
 

>まず前提として、複数選択状態で Backspace や Delete を押した場合の動作は
> if (すべての範囲が幅ゼロである) {
>  範囲外の文字を削除する; //  Backspace は左隣、Delete は右隣
> } else { // 幅1文字以上の範囲がひとつでも存在する
>  範囲内の文字を削除する; // 幅ゼロの箇所は影響を受けない
> }
>という仕様になっていると理解していますが、正しいでしょうか。

こういう仕様ということで正しいです。
そしてバグ情報ありがとうございます。
こちらでも再現させることができて、おかしなことになっていました。
V8.52で修正させていただきます。

[ ]
RE:08546 複数選択で delete/backspace でNo.08548
IKKI さん 15/02/20 01:29
 
秀丸担当さん、ご教示 & バグ修正ありがとうございました。
おかげさまで、作っていたマクロが期待通りに動くようになりました。
http://mobitan.org/hm/misc/#cmdJoinLines
ライブラリにもアップしておきます。
http://hide.maruo.co.jp/lib/macro/joinlines20.html

[ ]
RE:08536 ゼロ幅のカラーマーカーNo.08890
あべのり さん 15/12/29 15:17
 
あべのりです.今更ながらcolormarkerを使ったマクロを書き始めました.ここにあ
るゼロ幅カラーマーカーが消える現象でちょっと苦しんでいます.

(1)
>https://dl.dropboxusercontent.com/u/861457/20150206-002821.png
の右側のような動作を可能にする予定はありますでしょうか?colormarkerを「範囲
を表す」ものとして使おうとすると,この方が都合が良いです.

(2)
以下のマクロでカーソル位置がレイヤー名$$layerのカラーマーカー内にあるか
(カーソルがカラーマーカー末尾にある場合も含む)を確実に判定できますか?
$$u = getcolormarker(0x08 | 0x40,$$layer);
if($$u == "")$$u = getcolormarker(0x08,$$layer);
// $$u != ""ならばカラーマーカー内にある

(3)
(2)に関連してですが,getcolormarkerの0x40フラグはカラーマーカー末尾のみの取
得を可能にするものという理解で良いでしょうか.次のマクロでは$$uは空になるよ
うです.
colormarker -1,0xFF,0,0,1,$$layer,lineno,column,lineno,column+1;
message getcolormarker(0x08 | 0x40,$$layer);

環境はWindows 10 + 秀丸8.56β22です.よろしくお願いします.

[ ]
RE:08890 ゼロ幅のカラーマーカーNo.08895
秀丸担当 さん 15/12/29 16:19
 

(1)について、いまのところ考えてはいないです。
やるとしたらルールがとても複雑になることが予想されるのに対して、利用され
ることが少なく、バグや期待と違うことがあっても気づかないようなことになり
そうなのが心配ではあります。

(2)と(3)について、getcolormarkerはで0x40を指定する場合は、境界にある場合
はカーソルの左側(カラーマーカー末尾)にある色を取得します。
指定しない場合はカーソルの右側(カラーマーカー先頭)にある色を取得します。

例えば「赤赤青青」のようなテキストで、それぞれ赤と青に色がついているとし
ます。
境界の位置にカーソルがあるとして、0x40を指定しない場合は、青が取得され、
0x40を指定する場合は、赤が取得されます。
ヘルプが説明不足だと思うので追記させていただきます。

先頭の隣接でも末尾の隣接でもどちらでも取得するためには、getcolormarkerを
2回呼んで、0x40ありと0x40なしの両方で判断する必要があります。

[ ]
RE:08895 ゼロ幅のカラーマーカーNo.08900
あべのり さん 15/12/29 18:49
 
>(1)について、いまのところ考えてはいないです。
わかりました.

>(2)と(3)について、getcolormarkerはで0x40を指定する場合は、境界にある場合
>はカーソルの左側(カラーマーカー末尾)にある色を取得します。
>指定しない場合はカーソルの右側(カラーマーカー先頭)にある色を取得します。

なるほど,確かにそうなっていることを確認しました.(もう少し試してみるべきで
した.)ありがとうございます.

[ ]