/ 最近 .rdf 追記 編集 設定 本棚

脳log[20161129] TSV



2016年11月29日 (火)

最終更新: 2017-11-01T20:40+0900

[SakuraEditor] TSV

ただの表データを扱うのに Excelを起動したくはなくて、エディタの TSVモードを使った(※世の中には関連づけのままにフォトショップを使って画像をプレビューする人もいますがね<鷹揚な人だ見習おう)。タブ文字って、列の幅がタブの幅の倍数をまたぐたびに上下で列がずれるのが微妙で、タブを複数連続で使って揃えたとしても、列を編集して長さが変わったら限界まで圧縮されていたタブがぴょっこり復元して後ろの列を押し出したりするし、1タブでなんとかしようとタブの幅を想定最大列幅まで大きくするなら3つか4つの列しか画面に収まらないしで、どうやっても手間がかかる。タブ文字の拡張として TSVモードは良い。

 ここが気になる

TSVモードとは関係なく、いつもと違う使い方をしたのでこれまで気付かなかったことに気がついたり、新バージョンのバグに当たったりした。

  1. タブの幅を大きくするとあるレベル以上で幅が固定されて大きくならない
  2. 拡張子 .tsvで TSVファイル設定を作成したのに .tsvファイルを開いたときに TSVファイルタイプにならない
  3. (その状態で)タイプ別設定の内容を変更して決定しても反映されない
  4. (TSVモード) 列の余白が広すぎて収まるものまで画面の右にはみ出る
  5. (TSVモード) タブストップの再計算はいつ? (Alt、F、W、W で再読み込みするしかないの?<頻繁なので覚えた)

 1. タブの幅を大きくするとあるレベル以上で幅が固定されて大きくならない

文字の大きさや折り返し幅が関係してる。原因をつきとめてさあ報告しようかと既存のバグ報告を漁ろうとしたら、いきなり掲示板の3スレッド目がそれだった>開発掲示板(Unicode)[2370]。おまけ付きのパッチもある。脱力。

 2. 拡張子 .tsvで TSVファイル設定を作成したのに、.tsvファイルを開いたときに TSVファイルタイプにならない

MRUに前回の適用ファイルタイプが記録されていたせいだった。けっこう昔からある機能で、Ver.2系の最初のリリースぐらいからある。要望も昔からあるみたいで「Request/111 手動で変更したタイプを記憶 - SakuraEditorWiki」の投稿者は request.txtとなっている。

いらない機能です。「一時適用」したファイルタイプを一時でなく覚えておいた結果、ユーザー(俺)を欺いてしまっているのだから。先にばらしてしまうと、次に挙げる問題もこの機能に起因して引き起こされていたし、せめて、拡張子というルールに基づくファイルタイプ判別に引っかからなかった場合に限り、アドホックな一時適用ファイルタイプを採用するなどしてほしかった。

どちらを優先するのか一概に決められないところはある。アドホックな選択を記憶・復元するのがユーザーの意思を尊重した結果なら、ルールを新設して適用したいというのもユーザーの意思なのだから。お天気屋の原理主義者の今日の言い分は「ルールはルール(なので絶対)、一時は一時」ですがね。

※お天気屋たる所以は、かっこ書きの中身が普段は「(所詮はルール。人の作ったもの。文言の枝葉末節や不備に盲従せずその精神を忖度した運用でカバーし必要なら変えるべし)」だから。どちらへ転んでも自分に都合のいい主張になるという。

 3. タイプ別設定の内容を変更して決定しても反映されない

タイプ別設定一覧から選んで一時適用ボタンを押したときと、ファイルを開くときに MRUから記憶していたファイルタイプを復元したときでは、状態が違う。ついでにいうとコマンドラインの -TYPEスイッチでファイルタイプを任意に設定したときは、MRUから復元したときと同じ挙動を示す。

どういう挙動か。ファイルタイプの選択結果がロックされていないので、設定を変更したときに、拡張子に基づくルールベースの選択に上書きされる。

この結果どういうイライラ状況が発生したか。

  1. TSVファイル設定を作って拡張子 .tsvを登録(※「「ドット省略必須」「スペースorカンマorセミコロン区切り」のリスト(20100915p01)」であることはソースコードから既知)したのにテキストモードになる。なんで? 開き直してもエディタを再起動してもダメ。なんで?
  2. じゃあテキストモードでいいよ。テキストファイルの設定を TSVファイルの設定として使ってやる。設定変更、完了。全然反映されてないんだけど、なんで? (実はさっき複製して新設したばかりで未設定の TSVファイルタイプが拡張子に基づいて適用されていた)

適用されているファイルタイプってタイプ別設定一覧を表示するまでわからないんだよね。ちょっと覚えがあってヘルプを調べたらタイトルバーには表示できるみたいだけど(「$B タイプ別設定の名前 (sakura:2.0.7.0以降)」)、初期設定ではない。

ファイルタイプが見えないしすり替わってるしルールを無視するしで、気がつくまで(つまりソースコードを読むまで) 1と 2のあいだをぐるぐるぐるぐるしていたのだった。

 4. (TSVモード) 列の余白が広すぎて収まるものまで画面の右にはみ出る

どうも W の幅をひとつの基準にしているらしい(※高さの基準に x が、ハイフンの長さの基準に n や m が使われるように(<果たしてそれを聞き分けて区別できるだろうか)、もっとも幅広な文字として W が挙がるのかもしれない)。そして列の幅計算にはフォントを使わず、全角半角単位で数えてるっぽい。

たとえば最近愛用してる HG正楷書体-PRO フォントの場合、W の文字は全角半角で字形にゆらぎ程度の違いしかなく、基準に使う W は半角文字ながら全角の幅を持っている。その結果、列幅の想定と実際におよそ2倍の開きが生じた結果、ほぼ列の内容と同じ幅の余白が隣の列との間に確保されることになった。

そういうアルゴリズムで(=列幅の2倍の)タブストップを計算してるのだと思ったから、よーし好きに書き直しちゃうぞー、と CTsvModeInfo::CalcTabLength を頭から書き下していって途中で首を傾げてしまった(あれ? これ、俺が書こうとしてるそのまんまやん)。実際、列の内容が W のみだった場合は想定とのあいだに誤差はなく、計算された余白は適正と思えるものだった。

 5. (TSVモード) タブストップの再計算はいつ? (Alt、F、W、W で再読み込みするしかないの?)

実際どうなのだろう。閲覧を主に想定してるのだろうか。

 雑に解決します>TSVモードを富豪的に使いやすく。以前のファイルタイプを記憶しない。コマンドラインで指定されたファイルタイプをロックする。.rev2.patch

ファイル名の通り……ではないな。細かいけど「記憶しない」ではなく復元しないだし、一定以上のタブ幅が4に切り詰められることの修正も入ってる。

雑なパッチとは「Viewが前の行にさかのぼって再描画してくれない? 全部書き換えたらいいじゃない」の精神で。「Viewが持ってるフォント情報は古い、しょうがないので自分で作る」みたいなコメントがソースのどこかにはあって、フォントを利用した列幅計算はある状況では問題があるのかもしれないけど、「こまけぇこたぁ(略)」の精神で。

あと、TSVモードで現在無視されてるタブ幅の設定を、余白の量として扱った。タブ幅8なら隣の列とのあいだに最小の行で全角4個分の余白。

 @2016-12-09 やはり……雑!
  • 切り取ったときにゴミが残る
  • 横スクロールがときどき狂って右端が見えなくなる

我慢して使うよ。

 @2016-12-11 パッチを .rev2.patchに差し替えた
  • 雑な全体再描画誘導をアップデート(0を -1に置換しただけ)
  • 雑に折り返さないときの横スクロールに対応(常に最長行をフルスキャン)

下に書いた悪魔合体を進めると、横スクロール対応を雑でなくできる


P.S. histという用語は MoveHistPrev/MoveHistNext という前例に基づいたものだったらしい。マクロを書いていて気がついた。だからどうということではないが。

そして、マクロを書いていてアンドゥ履歴をまとめたいなとか、カーソルをコマンド連打以外の方法で操作したいなとか思って調べたら、そういう機能は 2.0.4.0とか 2.1.0.0で入っていたらしい。マクロ強化の動きは見ていたけど、これはたしかに必要だわ。理想的にはコマンド列挙の VBスタイルではなく JScript式のオブジェクトモデルが欲しいという考えは変わらないけど。コマンド群が不足なく整備されさえすれば、マクロの先頭で JScriptで書いたラッパーライブラリを読み込んで evalするという方法もありそうだけど。『C++のためのAPIデザイン』を読んだところでは、関数群を整備するのに比べてオブジェクトライブラリを用意することには特有の問題(依存とか修正とかに関してだっけ?)があって難易度が上がるとかなんとか(つまり最初は関数列挙で正解)。

書いていたマクロっていうのがカーソル行をファイル末尾に飛ばすものなんだけど、Ruby風に書くと File.lines.partition{...} というのをそのマクロを使ってやろうとしていたんだけど、どうしてもできないのがスクロール位置(縦と横)を動かさずにそれをすること。Jumpダメ。GoFileEndダメ。MoveHistPrevダメ。AddTailダメ。LineCutToStartダメ。DeleteLineは対象が折り返し行でダメ。使えるコマンドがなかった。「行削除って便利。(20100620p01)」のときから変わらない悩み。マクロ実行の前後でフォーカスを見失うのは我慢できない。

 PLAN TODO: 列を指定してソート

たしか秀丸にあったことで知った機能。今はソートの前に列を並び替えてるんだけど、ドロップをミスってデータを壊さないかはらはらしてる。気がつけばアンドゥできるけど、気がつかなかったときが怖い。

 PLAN TODO: 次のタブ(の前か後ろ)へ移動するコマンド

何度も[End]キーを押しては間違って行末へ飛んだ。どうやら TSVファイルの列を編集しているとき、無意識の[End]キーで飛んで行きたい末尾とは列の末尾だったらしい。

あと Tabキーで次の列へ移動するとか、Enterキーで下の行へ移動するとかしたい気がするけど、違和感なく適応できるかはわからない。試してみようにもマクロから CSV/TSVモードのオンオフを知る方法がないので。

 悪魔合体:CSVモードのための列幅キャッシュ + テキストを折り返さないときの最大行長キャッシュ (@2016-12-07)

できると思うんだけど。つまり、タブストップの随時再計算をそれなりの規模の CSVファイル(※ウチのPCだと富豪パッチバージョンで1MB・1万行ならもたつくけど現実的なレベルなので、それより大きいファイル)でやろうとして時間を空間で購った場合、横スクロールバーをドキュメントの最長行に対応させるためにやっていることっていうのはその一部になる。でもコストが整数2個から配列の配列と段違いに大きくなるので、CSVモードではない場合のコスト増を避けなければいけない。(折り返しモード)×(CSVモード)=4通りのコード分裂……。

レイアウトマネージャーの仕事の一部(※折り返さないときのための再計算を適宜実行すること)が外に漏れた結果あちこちにコピペコードが散らばってるなーとか、レイアウトはレイアウトマネージャーの持つパラメーターがないと幅計算できないしレイアウトマネージャーからは独立してる(参照を持っていない)んだから、レイアウトに計算結果を持たせるのは間違いだし値が意味を持たないよなーとか、レイアウトマネージャーからレイアウトを受けとってそのレイアウトにレイアウトマネージャーを引数として渡して幅計算をさせるっていうのは、直接レイアウトマネージャーに指定レイアウトの幅を聞けばいいし(※)、そうするならレイアウトマネージャーにはゲートウェイとして値をキャッシュして返す役割が期待できて、その値っていうのがつまり折り返さないときのために計算してレイアウトにこっそり(レイアウトの与り知らないところで)埋め込んだレイアウト幅のことなのだから、やっぱりレイアウトに値を埋め込むのは間違いだよなーとか、if (bFastMode) {...} で囲まれた複数の部分が幅計算の適宜実行だけじゃなく、CLayoutMgr::_DoLayout なんてアンダースコア付きで見るからにやばいメンバ関数を呼んでいて、レイアウトマネージャーの内臓に手を突っ込むがごとき癒着ぶりだなーとか、うーん、すでに悪魔の脳みそが必要な状態なんじゃないのこれ。

※こうなっていないのはレイアウトマネージャーが束ねるレイアウトっていうのがリンクリストになっていて、添え字で任意のレイアウトにランダムアクセスできないからだろうっていうのは想像できる。でも……。

落し所はレイアウトを必要なときに必要な分だけ作成して返すプロキシクラスに位置づけて、内部的にはレイアウトマネージャーの持つ情報に自由にアクセスすることだろうか。これだとレイアウトにレイアウトマネージャーへのポインタを持たせるオーバーヘッドを正当化できる、というか、それがレイアウトの持つ一番大事な情報ということになる。プロキシが自身が古くて無効だと知る方法はなんなのだろう。レイアウトマネージャーとレイアウトにバージョン番号を持たせて突き合わせるのだろうか。C++のイテレータはどうしてるかな……って考えると、俺だ! 俺が無効化されたイテレータを管理してた。デバッグモードでは違うけど。

でも……、レイアウトマネージャーの持つ情報のうち、レイアウト行ごとに分割できるものをまとめてレイアウトクラスとし、情報だけでなく行ごとに独立した機能もレイアウトのメンバーとしてレイアウトマネージャーからオフロードしたら、それって現状? プロキシクラスだなんだと言ってレイアウトマネージャーへのポインタを持たせる理屈をこねて、現状のレイアウトにポインタメンバを一個追加する言い訳を考えてただけ?

それもこれもレイアウトマネージャーを引数に取るレイアウトの一部のメンバ関数(CalcLayoutWidth, CalcLayoutOffset)と、レイアウトが自身で一切触れずにレイアウトマネージャーが操作するメンバ(m_nLayoutWidthとそのアクセサ)が気に入らないからだ。どちらもオフロード対象ではないということだから、レイアウトがレイアウトマネージャーへのポインタを持つか、レイアウトを通さず直接レイアウトマネージャーに尋ねて解決すべき問題。そもそも後者はメンバがメンバではないという一見してあり得ない状態。パブリックなメンバ変数しか持たない構造体ならそういう使われ方はあるが、じゃあお飾りでお為ごかしのアクセサを捨てよ。<こういうのって性格の反映なのかな。引きこもって外からの干渉を拒絶する姿勢、線を引いて私の仕事あなたの仕事と分ける姿勢って。人格のコアというか凝り固まってこじらせてるからこういう語調になるんだろうって。

……とまあこんな感じで脱線するから CSVモード(富豪パッチ版)の脱富豪化は進まないのだった。それにこういうのは実装前が一番楽しい段階だから。

<追記@2017-11-01>『[単行本(ソフトカバー)] 有野 和真【Androidを支える技術〈I〉──60fpsを達成するモダンなGUIシステム (WEB+DB PRESS plus)】 技術評論社』で解説される Androidのコードに、自分で使うわけではないレイアウトのためのメンバーが出てくる。よくあるらしい。レイアウトは親が子のサイズを知りたがり子が親のサイズを知りたがるので、すぱっと割り切れないし決めきれない。それを解決する方法も入り組んでいて難しい。</追記>

 @2017-08-18 矩形選択削除に問題

当然の利用法として、特定の列を矩形選択で一括削除したくなったりするだろう。ダメ。削除し過ぎたり削除し足りなかったりする。なんだろう。削除の途中でタブストップの再計算が走るのだろうか。

 @2017-08-28 TSV補助データのコスト

すぐ上で

タブストップの随時再計算をそれなりの規模の CSVファイル(※ウチのPCだと富豪パッチバージョンで1MB・1万行ならもたつくけど現実的なレベルなので、それより大きいファイル)でやろうとして時間を空間で購った場合、横スクロールバーをドキュメントの最長行に対応させるためにやっていることっていうのはその一部になる。でもコストが整数2個から配列の配列と段違いに大きくなるので、CSVモードではない場合のコスト増を避けなければいけない。(折り返しモード)×(CSVモード)=4通りのコード分裂……

と書いたけど、別に配列1個でもよさそう。その場合「横スクロールバーをドキュメントの最長行に対応させるためにやっていること」とは独立したデータになるのでコード分裂もない。どういうデータを持つか。

  • 1列目からN列目までのそれぞれで、最長の幅を持つのは何行目でその幅はなんぼか、というのを配列に記録する。
  • その記録された(列,行)の幅が縮んだときは、すべての行でその列の幅をスキャンして記録を更新する。
  • 列の編集によってある行が記録されている最長の幅を超えた場合は、その行と列の幅で上書きする。

スキャンの発生頻度はそこそこ低いのではないか。ネックは N番目の列の幅を網羅的にスキャンするときに、行の頭からスキャンして列番号をカウントしていくのでは後ろの方の列がつらいってこと。このへんはダブルクォーテーションの解釈と連携してさらなる補助データを用意することも考えられる。まあ、考えるだけで書かないんだけど……。