/ 最近 .rdf 追記 設定 本棚

脳log[SakuraEditor: 2017-09-08~]



2017年09月08日 (金) [SakuraEditor] ツールチップがおかしいよね。選択した単語のヘルプがマウスカーソルのそばに出る。そこじゃないよ。■メニュー>設定>キーワードヘルプ自動表示するを選ぶと(※この設定をソースコードで見つけて、どこで設定できるのか探した。iniには書き込まれない揮発性の設定みたいだ)、選択しなくてもキャレット付近の単語の説明が出るんだけど、ポインタの先の単語じゃないんだよね。ポインタのそばに説明が出るのに……。■ポインタがビューの上にないときには表示しない処理もあるんだよね。表示するのはポインタではなくキャレットのそばの単語の説明なのに。■誰かのこだわりの結果なのかなんなのか。普通じゃないね。■2.3.1.0で補完をすると、例えば サクラ を入力してから補完ウィンドウを出して サクラエディタ を選ぶと、サクラサクラエディタ みたいに補完されるのだけど、trunkをデバッグモードでコンパイルしたものがたまたまあるので試してみてもそんなことは起こらないから、何かの設定のせいなのか、よくわからないうちに直ったのか。■単語検索といえば昔のはてなダイアリーは豪快だった。サクラエディタも Trieとか接尾辞配列とかを導入したら、予め決めた単語境界に基づいて抽出した単語を登録済みキーワードと比較するのではなく、もっとアグレッシブに編集中のテキストから登録済みのキーワードを見つけ出せるのに。……とかいって俺には難しすぎるデータ構造だから他人事(ひとごと)だよ。手を動かして理解する手もある?

最終更新: 2017-10-01T01:05+0900

[SakuraEditor] ツールチップ>a.patch

  • ベースは https://svn.code.sf.net/p/sakura-editor/code/sakura -r 4196
  • +1007行, -760行。増えた……
    • CEditView.h, CEditView.cpp, CEditView_Search.cpp, CEditView_Mouse.cpp に限定すると +28行, -429行。圧倒的に減った(ファイルが2つ増えてるんだから多少はね)。
    • 新ファイル(AsWithHelpTooltip.h, AsWithHelpTooltip.cpp)合計が 714行。
    • 目的は減量ではなく機能単位での CEditViewからの切り出しだから……。
  • class CEditViewから消えたメンバー一覧
    • BOOL KeySearchCore(...); publicだが privateにできる。
    • bool MiniMapCursorLineTip(...); publicだが privateにできる。
    • enum LID_SKH {...} publicだが privateにできる。
    • BOOL KeyWordHelpSearchDict(...); publicだが privateにできる。
    • bool ShowKeywordHelp(...); public
    • DWORD m_dwTipTimer; public
    • CTipWnd m_cTipWnd; public
    • POINT m_poTipCurPos; publicだが privateにできる。
    • CDicMgr m_cDicMgr; publicだが privateにできる。
  • 代わりに継承元がひとつ増えた
    • class CEditView: public AsWithHelpTooltip<CEditView>
      • この名前ちょっと微妙? AsSelectable みたいなのを想定した命名規則なんだけど。
    • 基底クラスだけど仮想のデストラクタはない。インターフェイスではないし、public継承されたのもたまたまで、想定される使い方では必要ないはず。
      • 「public継承されたのもたまたま」

        理想を追求する人は private 継承して public 部に using AsWithHelpTooltip::XXX を並べるなり、継承ではなくコンポジションを選び一行だけの委譲関数を書くなりすると思う。というわけで、public 継承している現状に対応して AsWithHelpTooltip::OnTimerAsWithHelpTooltip::OnMouseMove をクラス外部から隠す目的で public から protected に変更するのは間違い。public と private と protected が混在することになるという一事をもって即座に間違いだと気付くべきところ。

      • protected 継承のことは知らない。virtual だとか菱形だとかの継承も難しすぎてわからない。
  • [新機能] メニュー>設定>キーワードヘルプ自動表示する を有効にすると登録キーワードをポイントするだけでヘルプツールチップが出る。
    • その際ツールチップの X座標は単語が始まる場所で、Y座標は注目している行の1.5行下。
    • キャレットを動かしたときも同じ位置に出せるんだけど、そこは互換性。知っていればポインタという定位置にヘルプが出るのも悪くないかもしれないし。
  • [違う所] ビューの境界ギリギリでツールチップを表示したあとポインタをビューの外に持っていくとツールチップが消えないことが本家ではあったがそれがない。
    • ミニマップ(これもビューの一種)のツールチップは必ず消えるんだけど、どういう仕組みによるのかはわからなかった。
    • ミニマップのツールチップはメインメニューが展開しているときにも出る。これは m_bInMenuLoopTRUE にならないからなんだけど、なぜミニマップだけなのか。
  • [気付いた点] CTipWnd::ComputeWindowSize()CTipWnd::DrawTipText() の中で DrawText() の引数 pszWorknew してるけど、DrawTextはサイズを指定する引数も受け取るから _T('\0') を埋める必要も new する必要もないよね。
    • というのが DrawTipText() というひとつの static 関数に反映されてる。

 何がしたかったのか

CEditViewの問題は(ANSI版よりずっと改善したとはいえ)あらゆる機能がひとつのクラスに同居していることなので、publicが privateにできたからといってなんの安心材料にもなりはしない。試したかったのは1機能1クラスで CEditViewを拡張する方法。20091129p01とか20131130。新機能はただの余録。そういうのも簡単にできるよねっていうお試し。

個別機能クラスが結局は CEditViewのすべて(継承元である他の個別機能クラスを含む)に依存できそうなのがちょっと予想外だった。機能と機能の絡み合いがだんだんと窮まってきそう。privateデータメンバー(※)を狭い範囲に閉じ込めて守りやすいのだけが利点か。

※publicデータメンバーもメンバーアクセスを代行するだけのセッタゲッタもありませんよ。それを当然の前提にして privateデータメンバーに注目してる。

 述懐

たまたま目についたひとつのメンバ変数 m_cTipWnd を分離してやろうと思って初めてキーワードヘルプや補完機能をセットアップしてから、コードの絡み合いを解きほぐして再構成するのに一週間ちかくかかってる。これでは、とりあえず一か所に、とりあえず public, friend にしたくなるのもわかる。あえてクラスを分けてアクセスを制限して、その結果に頭を悩ませたい人間がいるだろうか。いやいるし必要なんだけど、結局自分の能力でできる範囲のやり方しかできない。この a.patch だって自信満々で晒してるわけではなくて、むしろクラス設計は迷いまくりで、AsWithHelpTooltip.GetLastHitKeywordDictionary() なんて行き場のない妥協の産物である。正しい設計なんてのは与えられればそれ以外に考えられないくらい当たり前のものに思えるのに、自分でそこにたどり着くのは難しかったりする。高校数学の問題とかがそう。解法をチラ見すれば解ける。しかし独力ではなんのとっかかりも見つけられない。


2016年12月16日 (金)

最終更新: 2017-01-04T12:26+0900

[SakuraEditor] poor-awk.js (15.8KiB, 2017-01-04)

行単位で加工するマクロのための定型。そのままで同時に標準入出力にもファイルドロップ(SendToを含む)にも対応している。Perl譲りなんだろうけど、Rubyの -p スイッチって便利だし実際のニーズに基づいてるよねって。


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


2014年11月16日 (日)

最終更新: 2014-11-17T11:19+0900

[SakuraEditor] 登録キーワード強調@2ch

結局プログラム用なんだよね
漢字も強調できるけど、山田、太郎 と登録しても山田太郎はマークしない
山田商店も山田学校も全部別に登録しなきゃいけない
単語区切りをどう扱うかは、言語によって違う問題だから、
拡張子によって動作を変えれるとうれしい。

他との整合性がどうなるかは知らないけど強調キーワードの単語判定は sakura_core/view/colors/CColor_KeywordSet.cpp のモジュール内インライン関数(とそこから呼ばれる sakura_core/charset/charcode.h の IS_KEYWORD_CHAR)がやってるから、その判定にテーブルを使うようにして外部から与えられるようにすればタイプ別対応も不可能ではないよ、と書き込もうとしたけど、今は CWordParse::SearchNextWordPosition4KW に文字の分類と単語の切れ目検索を任せているのだった。たしか前に、できないことができるようになったという一事をもって満足してしまうのでこういう丁寧な仕事は俺には無理だなあとかここに書いてた気がするが、それだろうか。影響の見極めが面倒ですね。しがらみとか、調整とか、最も苦手なことだ。ぼっちメンタリティがコードの構成に影響するんだろうか。


2013年06月24日 (月)

最終更新: 2013-07-14T00:41+0900

[C++][SakuraEditor] Sakura Editor / PatchUnicode / #618 Singletonの実装変更

静的変数の初期化とマルチスレッド安全性。C++11より前と VCに関してはお寒い状況らしいが、そもそものコードが素朴なものだった。

もともとマルチスレッド対応ではないので静的変数で実装するのは大差ないとして(マルチスレッド対応コードを自分自身で書く機会を放棄することにはなる?)、唯一性の保証を TSingleton利用者に期待するというなら TSingletonはただグローバルアクセサを定義する手段になってしまう。この assertで従来通りの保証ができるかなと思ったんだけど

TSingleton(){ assert(this == static_cast<TSingleton<T>*>(T::getInstance())); }

static変数の初期化にロックがかかってたらこれはデッドロックを引き起こすんでない?ロックせず素通りしてしまうと未初期化オブジェクトを使用してしまうおそれがあるよね? C++11に対応した gccはどうやってるんだろ。

パッチの目的はデストラクタが呼ばれるようになることにあるらしいが、一方で、任意のタイミングでオブジェクトを破棄することはできなくなる。それはシングルトンオブジェクトにはそぐわない扱いかもしれないけど。

本当に stackoverflowのコードを利用するなら継承をやめたらいい。そうすれば TSingletonの利用者などというものは存在せずシングルトンクラスの実装者しかいなくなる。実装を提供するだけならマクロでもできるし、あえて継承にする理由は一元的にインスタンス作成を補足することではないかと。偽りの名前を与えられた中途半端な実装を別ファイルに隔離するのはどうかと思う。


3回ぐらいコメントの下書きをしてるけどぐるぐるしてまとまらなくてこの日記になる。


 @2013-06-25

インスタンス変数を関数内staticではなくクラス内staticにするとアドレスを取得するのに getInstanceを呼ぶ必要がなくなって、getInstanceとコンストラクタ呼び出しがネストしなくなってロックの可能性(そんなものが実在するかは gccがないので知らない)が消える、と思ったんだけど、すべてのシングルトンオブジェクトの初期化がプログラム起動時に走ってしまいそうだ。これを避けるとインスタンスをポインタで保持することになって、これは現在のコードだ。インスタンスを関数内staticで保持したまま、getInstanceでそのアドレスだけをクラス内static変数にコピーする、とかいうのはトリッキーなわりに利がなくて目的を見失ってる感。

クラスで実装するシングルトンってなんのためにあるんだろうね。アプリケーションクラスだけがシングルトンでそれ以外はそのメンバでいいやん。Javaとか C#の Main関数みたいな居心地の悪さがあるかしらんけどさ。


 @2013-07-12 クリティカルセクションは他スレッドを閉め出すもの。

同じスレッドであれば同じCRITICAL_SECTIONを引数にして何度でも EnterCriticalSectionできるらしい(同じ回数の LeaveCriticalSectionが必要)。

各スレッドはクリティカルセクションの所有権を取得した後は、自らの実行をブロックすることなく、EnterCriticalSection または TryEnterCriticalSection 関数を追加で呼び出すことができます。この結果、スレッドが既に自ら所有しているクリティカルセクションを待機しようとしてデッドロックに陥ることを防止できます。

というわけで、コンパイラが静的変数の初期化を実際どう実装するかは知らんけど、あまり気にせず上の方に書いた assertを使っていいんじゃないかな。

イベントを使ったのはこのとき(20130416)が初めてで、クリティカルセクションはまだ使ったことがないのでした。たぶんその存在はペゾルドさんの本で読んだんだろうなあ。9年前


2013年05月01日 (水) spamにこんな URL >http://keisok.sakura.ne.jp/moodle/user/view.php?id=2351■.sakura.ne.jpだから目にとまったんだけど、keisok.sakura.ne.jp自体はたぶん真っ当なサイト。moodleを狙って書き込みを行い、その一見無害な URLをスパムとしてまき散らしてるみたい。他にも /moodle/を含む URLを内容とするスパムが大量にあった。■こういうのは SaaSを利用する圧力になるんかな。自分で管理するより管理されてる方が楽だもんね。それが面白くない。

最終更新: 2013-06-11T17:42+0900

[SakuraEditor] DirectWriteパッチ(by novice123)

https://sourceforge.net/p/sakura-editor/patchunicode/482/

これは期待せざるをえない。でもこれは、何がいけなかったんだろう。

trunk2>svn revert -R .
trunk2>svn up -r 2954
(サクラエディタで trunk2_directwrite.patchを、改行をCRLFに統一して、保存し直し)
trunk2>patch -p0 < trunk2_directwrite.patch
(VS2008EEで sakura/sakura.slnを開く。Release_Unicodeでリビルド)

1.起動してファイルを開いた直後。[EOF]マークが残ってる。日本語文字に半角サイズしか割り当てられてないのはウチの環境のせい(Consolas+Meiryoで GDIが全角文字を半角で描画するせいで全角半角判定(GDI依存)と DirectWriteによる描画が食い違ってる)。

2.スクロールしてまた先頭に戻ってきた。ゴミが残ってる。

3.最小化して復元した。ゴミしかない。

自分でもやってるけど目に見えて遅くなるから@2013-05-26常用はしてない。関連(古い順)>2011042320120201(参考画像豊富)、201202052012070720121018。DirectWriteをハンコにして得られるメリットは縦方向のアンチエイリアスがかけられることかなあ。GDI+ClearTypeでも横方向のサブピクセルレンダリングはやってるから、フォントと OS次第ではそれ以外に差が出ない。サクラエディタではサブピクセル単位での字間調整に意味がないという個別の事情もあるし。


 @2013-05-02 背景画像を指定してみた。


 @2013-05-03 更新パッチ:trunk2_directwrite_a.patch

フォントは相変わらず Consolas+Meiryoなんだけど、日本語にきちんと全角幅が割り当てられてるのが不思議。

曲線を観察するに縦方向のアンチエイリアスがかかってない気がする。それでは Consolas+Meiryo+ClearTypeに比して DirectWriteのアドバンテージがない。

 Postボタンを押す直前に臆病風に吹かれてここに書きますよ。モチベーション大事。

早々に気にすることではないかも知れませんが、この二つの適切な呼び分けは難しくないですか?

HRESULT DWriteContext::SetLOGFONT(const LOGFONTW &logFont, float fontSize);
void    DWriteContext::SetFont   (const LOGFONTW &logFont);

ひょっとして上の2つと下の1つは副作用を伴う一連の不可分な処理を3分割したもので、入れ替えたり飛ばしたりできないものだったりするのでしょうか?

void DWriteContext::SetFont(HFONT hFont, bool bold, bool underline)

それから、DWriteContext::SetFont(HFONT hFont, bool bold, bool underline) の hFontにも太字・下線情報が含まれますが、CEditView_Paint.cppでこのように

HFONT hFont = GetFontset().ChooseFontHandle(false, false);
DWriteContext_SetFont(s_dwc, hFont, info.m_bFatFont, info.m_bUnderLine);

あえて無視する(無視させる)理由についても、想像がつかないもので、知っておきたいと思うのですが、教えてもらえますか?

 @2013-05-13 vim-jpのログを読んでる。

デジャヴを感じるコメントの流れだ。

>DirectWriteで描画したい · Issue #262 · vim-jp/issues · GitHub

そんで、すぐ上の疑問に関して。たぶん novice123さんはここまで読んでいない(手が回っていない)。

koron commented 4 months ago

最新版ではGetObject()を使ってHFONTからLOGFONTを取得することで、必要なときに DWriteContext_SetFont() するように変更しました。合わせて太字と斜体の有無は、DrawTextの引数ではなくフォントの情報を利用するように変更しました。

しっかし、個別問題に対処するパッチがいくつか見られるだけでさっぱりコードの全貌が追いかけられん。リンク切れも多すぎる。


 @2013-05-04 trunk2_directwrite_c.patch

ピクセルジオメトリはダイアログで設定するものでも、できるものでもないと思う。マルチモニタ。デバッグモード?


 @2013-05-13 下線

あんまり PPDとか理解してないけど結果オーライ(※自分の環境限定)で、こんな感じでやってた。(static_cast<int>(f)って int(f)って書けるんかなあ)

  STDMETHODIMP DWGdiTextRenderer::DrawUnderline(
      __maybenull void* clientDrawingContext,
      FLOAT baselineOriginX,
      FLOAT baselineOriginY,
      __in DWRITE_UNDERLINE const* underline,
      IUnknown* clientDrawingEffect
      )
  {
      HDC hdcRT = pRenderTarget_->GetMemoryDC();
      FLOAT ppd = pRenderTarget_->GetPixelsPerDip();
      RECT rcUnderline = {static_cast<int>(baselineOriginX*ppd), static_cast<int>((baselineOriginY+underline->offset)*ppd),
                          static_cast<int>((baselineOriginX+underline->width)*ppd)+1, static_cast<int>((baselineOriginY+underline->offset+underline->thickness)*ppd)+1};
      HBRUSH hBrush = CreateSolidBrush(GetTextColor(hdcRT));

      FillRect(hdcRT, &rcUnderline, hBrush);

      DeleteObject(hBrush);
      return S_OK;
  }

なんでペンでなくブラシなんでしょうねえ。思い出せないからもう一度考えてみると、ペンの場合、線の太さが、始点と終点で示される線分に対してどう影響するのかが不明だったからだろう。線がどのように太るのかが。例えば幅2ピクセルの水平線を引く場合、線の始点・終点の Y座標に大小どちらを指定すべきだろう? その点 RECTで領域を指定して塗りつぶせるブラシは結果がピクセル単位で予想できる。

 @2013-05-16 DPI、DIP。ドットパーインチ、デバイスインディペンデントピクセル。

COMのところの2、3項目しか読んでなかったけど、常識レベルの基礎が身につく良いシリーズだったのだね。DirectWriteの APIドキュメントを読んでると出てくる DIPについても書いてあった。

 @2013-05-17 QueryInterface

DirectWriteのサンプルプログラムがそうで、自分のも同じように AddRefがなかったことに、コメントした後で気付いたんだけど、

ds14050 2013-05-01

QueryInterfaceが正常に返る前に AddRefしておかなくていいのでしょうか?

この疑問に対して同シリーズのこれは無関係ではないのではないか。

hr = pFileOpen->QueryInterface(IID_IFileDialogCustomize, 
    reinterpret_cast<void**>(&pCustom));
if (SUCCEEDED(hr))
{
    // インターフェイスを使用する  (省略)
    // ...

    pCustom->Release();
}
else
{
    // エラーを処理する
}

通常どおり、戻り値 HRESULT をチェックしてメソッドの失敗に備えます。メソッドが成功した場合、ポインターを使用し終えた時点で Release を呼び出す必要があります。詳細については、「オブジェクトの有効期間を管理する」を参照してください。

  1. QueryInterfaceが返す<インターフェイスポインタのコピー>ごとに Releaseが呼ばれる。
  2. QueryInterfaceの呼び出し元は AddRefを呼ばない。
  3. ということは……

あと、キャストせずに thisを代入して返してるけど、どういうキャストの場合に thisが変化するか熟知してない自分には不安なコード。

「QueryInterface キャスト」で検索した。そうそう、こういう懸念、

既に示したQueryInterfaceの問題点は、どのようなインターフェースが渡されてもオブジェクトのアドレスをそのまま返した点です。 このとき、適切なインターフェースでオブジェクトをキャストしていれば、 インターフェースのvptrは適切なvtblを識別するようになります。

サクラエディタで実装する、IDWriteTextRendererを継承した GdiTextRendererに真っ当な QueryInterfaceや、InterlockedIncrement/InterlockedDecrementを使った AddRef/Releaseが必要かといえばいらないんだろうけど(だから動いてる)、ざる実装でも構いはしないんだけど、一見それっぽいのはミスリーディングでよろしくない(問題があるとして、ね)。


 @2013-05-19 _d.patchをベースにしたパッチを投稿したよ。

寝る前の3時間でやっつけた!(つもりになってるだけで返り討ち……とか)。

 @2013-05-20 最初のダメージ

どういう思考をたどったのか、地震(※震源は前住んでたとこだ)に揺れる風呂場で気がついた。まだバージョン管理されていない新ファイル3つがパッチに含まれていなかった。サイズが半分になってるのは小さすぎると思ったんだ。以前指摘されたのと同じミス。

 @2013-05-25 コメントを頂戴しました。

https://sourceforge.net/p/sakura-editor/patchunicode/482/?limit=20#c9f7

 @2013-05-25 trunk2_directwrite_e.patch

無意味な(そう見える@2013-05-27)関数分割(20130501p01.02.01)によって

  1. トラップのようなエントリポイントが生じていること
  2. 分割した関数に同じ名前(オーバーロード)や似た名前を与えていることによって名が体を表していない(似た関数を区別するのに十分な名前を与えていない)こと。
  3. その必要がないのにメンバ関数にすると、関数全体を見渡して thisが大量にもたらす入力パラメータ(※非constメンバ関数の場合は出力パラメータを兼ねる)を見つけ出すのが面倒くさい

という問題に対処した点は汲んでもらえなかったようだ。Cインターフェイスという名の、DWriteContextの opaqueポインタ読み方(ハンドル)を操作する限られた関数群をとっぱらって、DWriteContextクラス自体をモジュールの外へエクスポートしたことで事態はさらに悪化した。

クラスっていうのは内にも外にも過度の依存性/コンテクスト/癒着をもたらすのだなあ。どれだけ良い点、Cからの改善点を並べようと一部のクソのために C++を忌避するという選択は理解できる。

 @2013-05-26 補記。ペンディング。

あ、エクスポートにあたって private化はされてました。トラップは対外的には隠されていたし、呼び出し階層が明確になっていた。でも DirectWrite/DirectX系の定数をモジュールの外から隠せてない。レンダリングパラメータを int, floatと変換関数のセットで再定義してる意味がない。


チケットが pendingされてしまいました。遅いのは vim-jpが DirectWriteをオプション扱いにする理由として挙げていてわかっていたことだし、描画方法と描画先を両方とも GDIから Direct2Dに完全移行できれば改善が期待できるし、遅くても試したい人はいっぱいいたと思うのになあ。


 @2013-05-28 いまさらだけど

互換インターフェイスを C++で定義するならヘッダに書く内容は DWriteContextそのままではなくこんなんになると思う。

class DirectWrite
{
public:
  static DirectWrite* Init();

  DrawText();
  SetFont();
  SetRenderingParam();
  ...
  //< Initと対になる後片付け関数。
private:
  DirectWrite(); // 呼ばせない。代わりに Init().
  //< たぶんコピーコンストラクタも禁止しないといけない。
  //< ポインタメンバがあるので代入演算子も禁止しないといけない。

  struct DataMember;
  DataMember* m;
};

ポリモーフィズムが必要なわけでないしファクトリメソッドの代わりにコンストラクタでもいいのかも。それでもこっそりメンバの追加はできるだろうし。


 @2013-06-11 DirectWrite採用への布石。待望のパッチ

Sakura Editor / PatchUnicode / #588 文字列を一度にExtTextOutで描画する

ただ口を開けて待ってたわけではなかったんだよ(いいわけ)。「ところで、DispTextって文字の数だけ呼ばれるんだけど、こんなんを仮想関数にしても良いものだろうか。9か月前の自分はできるだけ長い GLYPH_RUNを対象にすることで回数を減らそうとしてたみたいだけど(20120201)」と書いていた自分はというと、CEditView_Paint.cppの似たような所をいじるまではやっていて、そこから、どいつにどうやって一挙に文字を書き出させるのが(どこでどこまで決めるのが)いいんだろう?というところで停止していた。

@2013-05-26 もしかしたら違う理由かも。過去の日記を読む限りでは、べらぼうに遅い→まあまあ遅い→まともな速度、と2段の変化があったようで、2段階で改善したことは記憶してなかったから。表示品質があまり変わらなかったのと細かい詰めを放り投げた結果として常用してないのかも。

@2013-05-27 SetFontと SetFontに関しては同じ機能を提供しつつ、LOGFONT版でなく HFONT版を呼んだときには、前回の HFONTとの比較結果次第で以降の処理を省略する意図がありそう。みみっちい。ポインタ/ハンドルを比較してその先の値が同じかどうかわかるかいな。そんな目的のためだけにメンバ変数を増やしますか。しかし、効くのだろうか。自分のパッチでは std::basic_string<TCHAR>を毎回作って lfFaceNameの比較をしていた。手を抜きすぎだとは思う。

読み方 オゥペイクだってさ。オパックだと思ってたよ。思い込みは恐いよ。

本日のツッコミ(全2件) ツッコミを入れる

もかスクリーンショット拝見しました。ExtTextOutのETO_OPAQUEようするに"背景を塗りつぶす"に相当する処..

ds14050背景画像を指定したスクリーンショットもとってみました。確かに仰るとおりでした。(novice123さんが背景画像を指..


2013年04月30日 (火) 警察と話をするときは新手の登場を黙って受け入れてはいけないな。途中から重量級を含めて3対1だよ。こちらを向かせる、座らせる、(自己)紹介をさせるなど、黙って耐えるのでなく、いろいろと試せる手はあったはずだ。相手だって威圧するのが目的で居るのではないはずだから(そういう建前がなかったらお手上げ)。ああ不愉快だ。

最終更新: 2013-05-02T16:45+0900

[SakuraEditor] 規制されたからここで。

830 :名無しさん@お腹いっぱい。:2013/04/30(火) 13:45:27.36 ID:ph+iG0mE0
>>829 
これってブロックコメントの色付け方法が変更されるの?違う? 
完全に閉じたときのみに色変更してほしいんだが……。 
現在の仕様のブロックコメント開始文字列があったらただちに色変更されるのは、(他人の)jsコード見るとき非常に困る。
>>830
829で指摘した変更は単なる最適化を目的としたものだよ。

>現在の仕様のブロックコメント開始文字列があったらただちに
これは ANSI板からの仕様みたいだし、自分としても、画面内の
情報だけに頼って色分けできたほうがどんなファイルを開いた場
合でも軽くていいと思う。対かっこ強調などもそういう割り切り
が多いよ。

せっかくこの日記に書いてるのだし、SHJS方式にも言及しておくと、あれでもやっぱり行末を超えた時点で、コメント終了マークが存在しなくても、コメント開始が確定してしまう。その取り消しを可能にするのがバックトラック版(20090808p01)なんだけど、あれができるのは Web上に載せるコード片が対象だからっていうのがある。IDEでもそれは許されるだろうけど、テキストエディタではどうだろう。バックグラウンドで色分けスレッドを走らせて非同期に画面上の文字の色を変えていくというのは可能だろうけど、わずかなラグ(ちらつき)が気になったりするだろうね。非同期だけど一瞬だけは待つことにして大体の場合は同期的に見えるというのだと、いいのかな? Operaだったか、ページの描画待ち時間を指定できたけど、あれはそういう意味だったのか。


予想外の展開。未完成の .jsに対する色分けかと思っていたら、XPathを表現した文字列に含まれる /* をコメント開始と誤認識するってな話だった。

832 :名無しさん@お腹いっぱい。:2013/04/30(火) 18:48:43.80 ID:ph+iG0mE0
>>831
?……そのプログラムって何を指すの?サクラ側の実行プログラムのこと?
例に挙げたjs(JavaScript)のこと?
jsでは「/*~*/」がブロックコメントという扱いだけど、「/*」の文字列はXPathなどコード上記述されることがままある。
このとき「*/」が以降のコードのどこかに記されてない限りコメント色が付いたまま。
だけどjsの実行上は問題ない。あくまで視認の問題。他人のコードを読むときに困ってるんだ。 

正規表現キーワードでは回避できないんだよね。既知の不具合です>サクラエディタBBS[7020]

掲示板のこのやりとりが 20090808p03のきっかけだった。自分では今でも使ってるんだけど、性能がときどき問題になる。バイナリファイル(=改行が少ない)を開いて検索したときに顕著。

もやもやするって書いちゃった(20130425)けど、このパッチ(#431 PHPヒアドキュメント/C++ RowString/C# quoted String等対応)が適用されたら、文字列の色分けを正規表現キーワードから組み込みのものにまた任せることができるし、それで解決すると思う。ただし、正規表現リテラルの中の /* は解決しない。


 @2013-05-02 vimだって?

834 :名無しさん@お腹いっぱい。:2013/05/02(木) 05:53:25.17 ID:8gLFxXSe0 
正規表現リテラルとか、htmlやphpみたいな文脈による言語変更とかを考えだすと限界があるんだよな・・・ 
vimのシンタックスエンジンを組み込みたい。あれかなり強力だし

どんなのだろうと検索したら見つかった。>syntax - vimdoc-ja< 難しすぎるでしょ。特定のキーワード(containとか contained)がフラットな記述に構造を与えてて余計に難しいってのもあるけど。


2013年04月14日 (日)

最終更新: 2013-04-15T03:23+0900

[SakuraEditor][Ruby] サクラエディタBBS[7601]

  • Editor定数の参照で落ちる。他の名前なら問題なし。
  • $Editorとすることで Editorオブジェクトが得られる。
  • マクロコマンドは先頭を小文字にしないと呼べない。(TraceOut→traceOutなど)
  • Editorオブジェクトに SCRIPTITEM_GLOBALMEMBERSフラグが付いてると、Rubyマクロのばあい ITypeLibが必要になる。(ITypeInfo->GetContainingTypeLibを実装する必要がある)
  • ITypeInfo->GetFuncDescがマクロコマンドの数(271)×3回、マクロを実行する前に呼ばれる。
  • 3回目に取得された ITypeInfoがリリースされない。
  • Editorオブジェクトもリリースが足りない。
  • 実行の成否にかかわらず一回につき 100KBぐらいメモリ使用量が増える。
  • リソースリークをなくしたければ参照カウントを捨てて、マクロ実行の前後ですべてのオブジェクトの作成・破棄を行うのが確実。
  • 12回目のマクロ呼び出しが必ず失敗する。例外が発生しているもよう。
  • TYPEATTR.cFuncsの数を減らしてマクロコマンドの数をごまかすと 11回を超えて成功するようになったり。リリースモードにしても回数は変化する。
  • 失敗しても繰り返しマクロを実行してるとときどき成功する。
  • マクロ実行に失敗するとき、IActiveScriptParse->ParseScriptText直前の IActiveScript->SetScriptState(SCRIPTSTATE_STARTED)呼び出しから足取りがつかめなくなる。
  • SetScriptStateを飛ばして ParseScriptTextを実行するようにすると例外はなく、正常にエラーメッセージ("実行に失敗しました")が表示される。なんにせよ失敗。
  • スクリプトエンジンをマクロ実行ごとにきれいに片付けられていれば、○回目から失敗みたいなことは起こらないと思うんだけど。なにが継続しているのか、あるいはこちらの何が破壊されていってるのか。

どうせ Rubyマクロはコマンドの先頭を小文字にする必要があって他のマクロと同じように書けないのだし、使われてないからこれまでに書かれたマクロ資産との互換性を図る必要もないし、例外とリークの元になり実行前の負荷も発生させる SCRIPTITEM_GLOBALMEMBERSフラグを落とすのがいいと思う。ScriptEngine名が "RubyScript." で始まるときとかに限って。

SCRIPTITEM_GLOBALMEMBERSフラグを落とすと $Editor.insTextだけのマクロは何十回でも問題なく実行できてるけど、それだけで安心してよいものか。Editorと書いたときのように(数字で始まるマクロコマンドを呼んだときも?)初回で確実に落ちるというだけならいいけど、何回も実行してるうちに運が悪ければ落ちるというのがあれば(たち)が悪い。


2012年10月23日 (火) 役に立つ機会があるかはともかく、least negative valueという表現。そんなに、ネガティブ(負)じゃない、値。-35と -35.5では -35が LNVらしい。※言及リンクは省略しました。

最終更新: 2012-10-24T00:56+0900

[SakuraEditor] またやらかしてたみたい。これ→20091026p01

矩形の選択中の未選択状態でコピーをすると矩形のままになる - ID: 3578282

IsBoxSelectingは IsTextSelectingに含まれる概念だと思われるから IsTextSelecting(0幅選択を含む選択状態)と IsTextSelected(1文字以上選択されている)の2つだけで判定条件を書きたい。0文字選択→選択キャンセル&移動。1文字以上選択→選択キャンセル。実際の処理は共通でいけるはず。

IsTextSelectingは BoxSelectが必ずロックを伴うことを当てにして今のように書かれてる気がするので追い追い修正が必要になりそう。>20090923p01

でも黙っとく。実際に正しく動くか検証もしてないし。


2012年10月02日 (火) すすぐ、というのは不純物の入った水を万倍の万倍の万倍に薄めて無いも同然になりました、ってこと? 理科の先生が一生懸命いつまでもこするより回数を増やした方がいいって言ってるのを見た記憶があるけど、万にかかる係数を増やすより指数を増やした方が効率がいいって事?■検索……。「洗浄の回数について - 質問・相談ならMSN相談箱」 同量の水を小分けにして回数を増やす。溶解の速度。廃液を減らす。初めて聞いた言葉>コンタミ(contamination)。

最終更新: 2012-10-02T16:53+0900

[SakuraEditor] マクロのお試しとかデバッグに。

選択された文字列かファイルの全体を JScriptマクロとして実行する。登録の手間なしにマクロを試してみたり、マクロのデバッグ&実行を登録や読み直しの手間なしに行うために。何個目の車輪になるのか数え切れないほどだと思ったので書いてなかったが、今日読んだ本*に同じようなアイディアが書いてあったので。2010年作。

/*
	InstantMacro.js
	選択テキストまたはファイル全体を JScriptマクロとして実行する。
*/

var jsmacro = Editor.GetSelectedString(0) || Editor_AllText();
try {
	eval(jsmacro);
} catch(e) {
	Editor.TraceOut(e.message);
}

function Editor_AllText()
{
	var text = "";
	for(var n = 1, n_max = Editor.GetLineCount(0); n <= n_max; ++n) {
		text += Editor.GetLineStr(n);
	}
	return text;
}

* リンク先は今日読み終わった本。このマンガを読んでも出てこない。


2012年08月09日 (木) [Vista] 驚愕の事実。「Windows 8でもっとも進化したもの、それはマインスイーパ | スラッシュドット・ジャパン」Vistaを5年以上使用してきてマインスイーパを今日初めて起動した!ピンボールに次いで、フリーセルと同じくらいよくやってたのに。■ Vistaに限れば一番よく起動したのは上海だった。Flappyと同じくらい懐かしさを感じるゲームだ。

最終更新: 2012-08-10T23:22+0900

[SakuraEditor] cacheExt.xmlから history要素を取り除くときにどれだけのサイズ削減効果があるのかを把握していちいち喜びを感じたいが検索によって選択された領域のステータスバー情報が一瞬で消えてしまうので

Index: sakura_core/CViewCommander.cpp
===================================================================
--- sakura_core/CViewCommander.cpp	(リビジョン 55744)
+++ sakura_core/CViewCommander.cpp	(リビジョン 55745)
@@ -3035,9 +3035,6 @@
 			//	2005.06.24 Moca
 			m_pCommanderView->GetSelectionInfo().DisableSelectArea( bReDraw );
 			m_pCommanderView->GetSelectionInfo().SetSelectArea( matchLayoutRange );
-			if( bReDraw ){
-				m_pCommanderView->GetSelectionInfo().DrawSelectArea();
-			}
 		}
 
 		/* カーソル移動 */
@@ -3046,6 +3043,15 @@
 		GetCaret().MoveCursor( matchLayoutRange.GetFrom(), bReDraw );
 		GetCaret().m_nCaretPosX_Prev = GetCaret().GetCaretLayoutPos().GetX2();
 
+		if( bReDraw && !keepCurrentSelection ){
+			// keepCurrentSelectionのときは不要っぽい。
+			// ステータスバーに表示される選択領域のサイズが
+			// (たぶんカーソル移動によって)消去されないように
+			// 後ろの方に移動してきた。
+			/* 選択領域描画 */
+			m_pCommanderView->GetSelectionInfo().DrawSelectArea();
+		}
+
 		/* メッセージ */
 		if( PointCompare( originalSearchPos, searchPos ) < 0 ) {
 			m_pCommanderView->SendStatusMessage(_T("▲末尾から再検索しました"));
@@ -3218,10 +3224,6 @@
 		} else { // マッチ範囲で選択範囲を置き換える。
 			sel.DisableSelectArea( bRedraw );
 			sel.SetSelectArea( matchLayoutRange );
-			if( bRedraw ){
-				/* 選択領域描画 */
-				sel.DrawSelectArea();
-			}
 		}
 
 		/* カーソル移動 */
@@ -3230,6 +3232,15 @@
 		GetCaret().MoveCursor( matchLayoutRange.GetFrom(), bRedraw );
 		GetCaret().m_nCaretPosX_Prev = GetCaret().GetCaretLayoutPos().GetX2();
 
+		if( bRedraw && !keepCurrentSelection ){
+			// keepCurrentSelectionのときは不要っぽい。
+			// ステータスバーに表示される選択領域のサイズが
+			// (たぶんカーソル移動によって)消去されないように
+			// 後ろの方に移動してきた。
+			/* 選択領域描画 */
+			sel.DrawSelectArea();
+		}
+
 		/* メッセージ */
 		if( PointCompare( originalSearchPos, searchPos ) > 0 ) {
 			m_pCommanderView->SendStatusMessage(_T("▼先頭から再検索しました"));

2012年07月01日 (日)

最終更新: 2012-07-01T14:27+0900

[SakuraEditor] CEditView::IsSearchStringという無茶な関数。

  • 検索条件を thisで与え、検索対象を引数で与える。(なぜ検索対象が可変?そのせいで対象となる文字列(連続したバッファ)が必要で、将来的に複数行検索に対応できない)
  • 仕様(出力)が検索オプションで変わる。
  • 検索コードを呼ぶのでなく自分でマッチ判定をしてる。(コピペコード)
  • 検索語ハイライトを行う CColor_Foundからしか呼ばれていない。
  • (関数の目的から)一文字一文字呼ばれることを想定しているが、検索情報を蓄えたりせず(できず)、その場その場で判断している(無駄が多い)。CColor_Foundの方が、行が変わるごとに通知があり行末に向かって順次実行されることがわかっているぶん戦略的に動ける。

というわけで、CEditView::IsSearchStringからコピペコードを廃すると CColor_Foundが知りたがっている何番目の単語がマッチしたのかを知るためにさらなる検索が必要になるにもかかわらず、CEditView::IsSearchStringという変態のために仕様を決める必要は全然ない気がしてきた。仕様ってのはこれの→「sakura-unicode:1830」「複数単語検索のブックマーク・Grep対応 - ID: 3539115」。CEditView::IsSearchStringには消えてもらって CColor_Foundと CSearchAgent::SearchWordの組み合わせで何とかすべき問題ではないかと。SearchWordだと検索結果が改行をまたぐようになってもそのままで適応する利点もある(CColor_Foundと CColorStrategyの方には対処が必要だが)。


2012年06月22日 (金)

最終更新: 2012-12-05T05:04+0900

[SakuraEditor] InsTextマクロでドキュメントを変更しても(変更)フラグが表示されない。 (BugReport/99) - SakuraEditorWiki

前に自分が引っかかったのと同根。「タブやタイトルバーに変更フラグが表示されるように修正した。フラグ自体は立ってたけど描画が行われていなかったというミス。(20100620p01)

変更フラグを管理してるのは CDocEditor。ドキュメントを変更した関数が SetModified(bool modified, bool redraw)メソッドでこれに通知するが、更新フラグが立ったとき(落ちたとき)に描画が抑制されていると、以後ドキュメントの変更をトリガにしたキャプションの変更機会を失う。キャプションの内容は変数展開、条件分岐を利用して高度にカスタマイズが可能で、キャプションの更新を必要最低限にケチるのは理に適ってる。ではどうするか。描画の抑制を描画の遅延ととらえて、抑制中に加えられた描画に影響する変更を記録して、後でまとめて描画する? Webブラウザが JavaScriptに対して行ってるように? それは大変。マクロ由来のコマンドを描画を抑制して実行してるのは CMacro。かといって CMacroがコマンド呼び出しの前後で描画を適切に維持するのは出過ぎた行為。さっき「キャプションの更新を必要最低限にケチるのは理に適ってる」と書いたけど、描画の抑制をやめるのが一番簡単。だって Charマクロで InsTextのような問題が起きないのは、Charマクロの実体関数が redrawフラグを受け取らないで常に描画を行ってるせいだから。

 余談: InsTextで何をしていたのか。

<2012-06-25> ReDrawマクロの存在を教えてもらったのでちょっと修正。なんでこんな(描画に問題があるときに実行して下さい)コマンドがあるんだか。

現在の編集ウィンドウを再描画します。何らかの原因で画面表示を最新状態に更新したい場合に使います。

【例】 ステータスバーを非表示にしたとき、メニューバーの左側にステータス情報が表示されますが、ファイル名のチップ等で消されてしまうことがあります。そういう場合に使います。

なんで手動でやらそうとしたんだ……。 </2012-06-25> <2012-07-04> ……って、その、メニューバーの左側右端に表示されるステータス情報は、もう消えなくなってるアレだ。「SourceForge.net Repository - [sakura-editor] Revision 1594</2012-07-04>

/** OneTouchIndent.js
 * サクラエディタのマクロ。
 * Tabキーに割り付けて使う。
 * 前の行にならって同じインデント(Tab,Spaceなど空白文字全般)を挿入する。
 * Enterをトリガーにしたオートインデントってイマイチじゃない?
   一段深すぎたり、空行を挿入したかったりするときに削除の手間が必要で。
   このマクロは、Tabキーを押す手間は必要だが、望まないインデントは行わない。
*/

if (OneTouchIndent()) {
	Editor.ReDraw();
} else {
	Editor.IndentTab();
}

function OneTouchIndent() // returns true(done) or false(undone)
{
	// 文字列選択中はインデント文字の挿入ではなく、インデントを行いたい。
	if (Editor.IsTextSelected) {
		return false;
	}

	var caret = eval(Editor.ExpandParameter("({y:$y-1, x:$x-1})"));

	// 前の行にならってインデントの文字と量を決定するので、先頭行では(デフォルトの)インデントを行いたい。
	if (caret.y < 1) {
		return false;
	}

	var thisline = Editor.GetLineStr(caret.y+1);
	var prevline = Editor.GetLineStr(caret.y);
	var previdnt = prevline.match(/^(?:(?![\r\n])\s)*/)[0]; // 行頭の、改行を除く空白文字

	// prevline[0...previdnt.length]
	// thisline[0...caret.x]
	if (previdnt.length <= caret.x) {
		return false;
	}
	if (previdnt.substring(0, caret.x) !== thisline.substring(0, caret.x)) {
		return false;
	}

	Editor.InsText(previdnt.substr(caret.x));
	return true;
}

 @2012-12-05

気付くのが遅すぎるけど、インデントを深くしたいときには意識的にタブキーとスペースキーを使い分けないといけなかった。そういうのを気にせずワンボタンで済ませられたらなお良かった。