最終更新: 2018-02-15T12:43+0900
メモ帳でできるように……と書くために今試してみたのだけど、挿入されたのが「00:49 2017-12-17」だった。なんだこのキモい並びは。年月日も時刻もフォーマットこそ OSの地域と言語のオプションに従ってはいるものの、その前後関係が決定的にYMD文化圏のそれではない。
メモ帳はどうでもいい。サクラエディタには「日付を挿入」コマンドと「時刻を挿入」コマンドがある。それぞれのフォーマットをカスタマイズできる。日付を挿入するコマンドを F5 に割り当てて、そのフォーマットを「yyyy-MM-dd HH:mm」にすればいいと考える。できない。日付書式と時刻書式が分かれていて同時に使用できないせい。
ソースを見れば一目瞭然だけど、Windowsが GetDateFormatと GetTimeFormatという2つの APIを公開していて、サクラエディタのコマンドがその薄い被せ物だというのが、おそらくこの不便な分離の理由。くっつけたっていいでしょ。
カスタムフォーマットの場合に限り GetDateFormatと GetTimeFormatの両方の APIを呼ぶ。そうでないと OSの設定で日付フォーマットを「'hh' yyyy'年'M'月'd'日'」みたいにしたときに hh がそのまま hh とは表示されなくなる。
書式文字列と書き込み先が同じポインタでも問題ないみたい。フォーマットに対して書き込み文字数の方が多い場合があるとしたら問題になるだろうか。ANSI版で日本語の曜日が書き込まれるときとか?
GetTimeFormatは時刻だけしか対応していなくて、日付を整形したい場合はGetDateFormatというAPIを使うことになります。これが2つに分かれている所為で、両方を一度に整形したい場合に実に厄介な問題が発生するんですが、まぁそれは置いておいて………。
置いておかないで。知りたい。
「tt」が「AM」になるのか「午前」になるのかは、実行したOSの設定次第です(日本語設定なら「午前」とか)。
それでは、日本語環境で「t」はどうやって表示されるのでしょう。
「AM」が「A」、「PM」が「P」に対応するみたいで、なんとなく「t」は「tt」の短縮形じゃないかと想像付くんですけど、果たして、「午前」「午後」はどのように短縮されるんでしょうかね…。
wwwwww
20170908p02で書いた明示的デストラクタ呼び出しと placement new の組み合わせがここでも。そんなにありふれてるかなあ。
20170908p02で結論が出てるけど、一時オブジェクトとスワップするのが最適解。ナンセンスに思えた途中の試行が実はいいセンいっていたという。さらに最近の C++ なら素直に移譲コンストラクタが使える(らしい)。
約200行ほどの関数です。別にどうと言うことの無い関数なんですが(個人的には幾つかに分割したいんですけど)、何故にinlineになっているのかっ!
インライン展開するには大きすぎるから。って言うか、そもそもインライン展開する必要ないでしょ。プログラムの起動時に1回だけ呼ばれる初期化用の関数なんだし。
自分がわりと inline を、とりあえず付けておこうっていう人間かな。このファイル(脳log[20170908p01] ツールチップ>a.patch)の中を inline で検索すると知れると思うけど。
基準は、今考えてるんだけど、以下のいずれか。
最近 C++ In Depthシリーズを読み返してるんだけど、そして実はシリーズに見えてシリーズとは書かれていないんだけど、『Efficient C++』の中に「シングルトン関数」という呼び名で3番目の関数のインライン考が載せられていた。同意するかは読み手に任されるとしても、そういう考えのもとにインライン化することもあるってことで。
2番目の「実質的に小さい」は、よく考えると嘘かもわからんね。呼び出す関数の引数が多かったり、呼び出すつもりの関数がインライン展開されたりすると、ソースコード上の見た目以上に大きいということがわりにありそう。
他の関数を真似してinlineをつけたんだと思うけど、「訳がわからんけど動くからそれで良いや」的にコンパイラを誤魔化すだけのテクニックなんて使用厳禁っ!
最近知ったんだけど、inlineを付けるだけで関数の多重定義によるリンクエラーが避けられるらしいですねえ>20170410。そういうことをしだすと、「道具として使う」を通り越して乱用の域に達してると思う。
pHを測る機械があって、ピッと簡単に数字が出るのかは実際に使わなかったから知らないけど、「原理も知らずに……(もにょもにょ)」というぼやきが聞こえてきたのが印象に残ってる。原理を知らないと手段の優劣も限界も条件にあった適切な選択もわからんもんね。
道具ってのは元をたどれば誰かが目的のために最適な手段を求めた結果として生み出された経緯があると思う。予め用意された道具を使っているだけではアマチュアの域を出られないのではなくて? 知の高速道路に関係する話。開拓者とフォロワーの違い。大多数にはハイアマチュアも届かない存在かもしれんけど。
日付と時刻を同じひとつのフォーマットで指定できる。そのソースがたぶんここ>「HRESULT tePSFormatForDisplay(PROPERTYKEY *ppropKey, VARIANT *pv, DWORD pdfFlags, LPWSTR *ppszDisplay)」
[hHmst]が時刻を表すフォーマット文字としてリストでき、[dMyg]が日付の文字としてリストできる。時刻文字が見つかると日付文字が見つかるまで、日付文字が見つかると時刻文字が見つかるまで API 呼び出しを遅延し、切り替わる部分まで(もしくは文字列末尾まで)をフォーマット文字列として、対応する API を呼び出している。どんなフォーマットでも最低1回は GetDateFormat を呼び出してるっぽいのは、2値だと if 文の条件がシンプルに書けるからか。必ず1度は、それも最初に日付フォーマット文字に出くわすだろうという推定は、すごく妥当だと思います。
これが真摯な対応というべきものか。俺は2割のコーナーケースを捨てて過度にシンプルな解法を選びがち。
先に挙げたコードの通りの回答。たぶん AM
とか PM
とか Thu
とか Sat
とか Oct
とかに展開された文字列が再度フォーマット文字として解釈されてしまうんだろう。まじめに挙げると問題のあるロカールでは問題ばかりだった。
サクラ
を入力してから補完ウィンドウを出して サクラエディタ
を選ぶと、サクラサクラエディタ
みたいに補完されるのだけど、trunkをデバッグモードでコンパイルしたものがたまたまあるので試してみてもそんなことは起こらないから、何かの設定のせいなのか、よくわからないうちに直ったのか。■単語検索といえば昔のはてなダイアリーは豪快だった。サクラエディタも Trieとか接尾辞配列とかを導入したら、予め決めた単語境界に基づいて抽出した単語を登録済みキーワードと比較するのではなく、もっとアグレッシブに編集中のテキストから登録済みのキーワードを見つけ出せるのに。……とかいって俺には難しすぎるデータ構造だから最終更新: 2017-10-01T01:05+0900
https://svn.code.sf.net/p/sakura-editor/code/sakura -r 4196
BOOL KeySearchCore(...);
publicだが privateにできる。bool MiniMapCursorLineTip(...);
publicだが privateにできる。enum LID_SKH {...}
publicだが privateにできる。BOOL KeyWordHelpSearchDict(...);
publicだが privateにできる。bool ShowKeywordHelp(...);
publicDWORD m_dwTipTimer;
publicCTipWnd m_cTipWnd;
publicPOINT m_poTipCurPos;
publicだが privateにできる。CDicMgr m_cDicMgr;
publicだが privateにできる。class CEditView: public AsWithHelpTooltip<CEditView>
AsSelectable
みたいなのを想定した命名規則なんだけど。「public継承されたのもたまたま」
理想を追求する人は private 継承して public 部に using AsWithHelpTooltip::XXX
を並べるなり、継承ではなくコンポジションを選び一行だけの委譲関数を書くなりすると思う。というわけで、public 継承している現状に対応して AsWithHelpTooltip::OnTimer
と AsWithHelpTooltip::OnMouseMove
をクラス外部から隠す目的で public から protected に変更するのは間違い。public と private と protected が混在することになるという一事をもって即座に間違いだと気付くべきところ。
m_bInMenuLoop
が TRUE
にならないからなんだけど、なぜミニマップだけなのか。CTipWnd::ComputeWindowSize()
と CTipWnd::DrawTipText()
の中で DrawText()
の引数 pszWork
を new
してるけど、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()
なんて行き場のない妥協の産物である。正しい設計なんてのは与えられればそれ以外に考えられないくらい当たり前のものに思えるのに、自分でそこにたどり着くのは難しかったりする。高校数学の問題とかがそう。解法をチラ見すれば解ける。しかし独力ではなんのとっかかりも見つけられない。
最終更新: 2018-03-14T23:15+0900
行単位で加工するマクロのための定型。そのままで同時に標準入出力にもファイルドロップ(SendToを含む)にも対応している。Perl譲りなんだろうけど、Rubyの -p スイッチって便利だし実際のニーズに基づいてるよねって。
マクロに登録しても良し、ソートしたいテキストファイルをドロップしても良し。
よく知らないけどプラグインだとメインメニューにコマンドを登録できたりするんだろうか。こういうものを C++で書いてエディタ本体に内蔵したくはないよね。
掲示板に貼り付けるには長いし、Wikiのマクロ投稿ページはログインを要求してきて書き込みさせてくれないしで、返信できずにここに書いてるんですよ。>「[8316] ソートの挙動の切り替えはサクラエディタ内で可能になりませんでしょうか」
Meryのソートマクロを見たら実質ワンライナーだった。連打するたぐいのマクロではないとしても、自分のはゼロオーバーヘッドルールから離れすぎていた。
最終更新: 2017-11-01T20:40+0900
ただの表データを扱うのに Excelを起動したくはなくて、エディタの TSVモードを使った(※世の中には関連づけのままにフォトショップを使って画像をプレビューする人もいますがね<鷹揚な人だ見習おう)。タブ文字って、列の幅がタブの幅の倍数をまたぐたびに上下で列がずれるのが微妙で、タブを複数連続で使って揃えたとしても、列を編集して長さが変わったら限界まで圧縮されていたタブがぴょっこり復元して後ろの列を押し出したりするし、1タブでなんとかしようとタブの幅を想定最大列幅まで大きくするなら3つか4つの列しか画面に収まらないしで、どうやっても手間がかかる。タブ文字の拡張として TSVモードは良い。
TSVモードとは関係なく、いつもと違う使い方をしたのでこれまで気付かなかったことに気がついたり、新バージョンのバグに当たったりした。
文字の大きさや折り返し幅が関係してる。原因をつきとめてさあ報告しようかと既存のバグ報告を漁ろうとしたら、いきなり掲示板の3スレッド目がそれだった>開発掲示板(Unicode)[2370]。おまけ付きのパッチもある。脱力。
MRUに前回の適用ファイルタイプが記録されていたせいだった。けっこう昔からある機能で、Ver.2系の最初のリリースぐらいからある。要望も昔からあるみたいで「Request/111 手動で変更したタイプを記憶 - SakuraEditorWiki」の投稿者は request.txtとなっている。
いらない機能です。「一時適用」したファイルタイプを一時でなく覚えておいた結果、ユーザー(俺)を欺いてしまっているのだから。先にばらしてしまうと、次に挙げる問題もこの機能に起因して引き起こされていたし、せめて、拡張子というルールに基づくファイルタイプ判別に引っかからなかった場合に限り、アドホックな一時適用ファイルタイプを採用するなどしてほしかった。
どちらを優先するのか一概に決められないところはある。アドホックな選択を記憶・復元するのがユーザーの意思を尊重した結果なら、ルールを新設して適用したいというのもユーザーの意思なのだから。お天気屋の原理主義者の今日の言い分は「ルールはルール(なので絶対)、一時は一時」ですがね。
※お天気屋たる所以は、かっこ書きの中身が普段は「(所詮はルール。人の作ったもの。文言の枝葉末節や不備に盲従せずその精神を忖度した運用でカバーし必要なら変えるべし)」だから。どちらへ転んでも自分に都合のいい主張になるという。
タイプ別設定一覧から選んで一時適用ボタンを押したときと、ファイルを開くときに MRUから記憶していたファイルタイプを復元したときでは、状態が違う。ついでにいうとコマンドラインの -TYPEスイッチでファイルタイプを任意に設定したときは、MRUから復元したときと同じ挙動を示す。
どういう挙動か。ファイルタイプの選択結果がロックされていないので、設定を変更したときに、拡張子に基づくルールベースの選択に上書きされる。
この結果どういうイライラ状況が発生したか。
適用されているファイルタイプってタイプ別設定一覧を表示するまでわからないんだよね。ちょっと覚えがあってヘルプを調べたらタイトルバーには表示できるみたいだけど(「$B タイプ別設定の名前 (sakura:2.0.7.0以降)」)、初期設定ではない。
ファイルタイプが見えないしすり替わってるしルールを無視するしで、気がつくまで(つまりソースコードを読むまで) 1と 2のあいだをぐるぐるぐるぐるしていたのだった。
どうも W の幅をひとつの基準にしているらしい(※高さの基準に x が、ハイフンの長さの基準に n や m が使われるように(<果たしてそれを聞き分けて区別できるだろうか)、もっとも幅広な文字として W が挙がるのかもしれない)。そして列の幅計算にはフォントを使わず、全角半角単位で数えてるっぽい。
たとえば最近愛用してる HG正楷書体-PRO フォントの場合、W の文字は全角半角で字形にゆらぎ程度の違いしかなく、基準に使う W は半角文字ながら全角の幅を持っている。その結果、列幅の想定と実際におよそ2倍の開きが生じた結果、ほぼ列の内容と同じ幅の余白が隣の列との間に確保されることになった。
そういうアルゴリズムで(=列幅の2倍の)タブストップを計算してるのだと思ったから、よーし好きに書き直しちゃうぞー、と CTsvModeInfo::CalcTabLength を頭から書き下していって途中で首を傾げてしまった(あれ? これ、俺が書こうとしてるそのまんまやん)。実際、列の内容が W のみだった場合は想定とのあいだに誤差はなく、計算された余白は適正と思えるものだった。
実際どうなのだろう。閲覧を主に想定してるのだろうか。
ファイル名の通り……ではないな。細かいけど「記憶しない」ではなく復元しないだし、一定以上のタブ幅が4に切り詰められることの修正も入ってる。
雑なパッチとは「Viewが前の行にさかのぼって再描画してくれない? 全部書き換えたらいいじゃない」の精神で。「Viewが持ってるフォント情報は古い、しょうがないので自分で作る
」みたいなコメントがソースのどこかにはあって、フォントを利用した列幅計算はある状況では問題があるのかもしれないけど、「こまけぇこたぁ(略)」の精神で。
あと、TSVモードで現在無視されてるタブ幅の設定を、余白の量として扱った。タブ幅8なら隣の列とのあいだに最小の行で全角4個分の余白。
我慢して使うよ。
下に書いた悪魔合体を進めると、横スクロール対応を雑でなくできる
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)」のときから変わらない悩み。マクロ実行の前後でフォーカスを見失うのは我慢できない。
たしか秀丸にあったことで知った機能。今はソートの前に列を並び替えてるんだけど、ドロップをミスってデータを壊さないかはらはらしてる。気がつけばアンドゥできるけど、気がつかなかったときが怖い。
何度も[End]キーを押しては間違って行末へ飛んだ。どうやら TSVファイルの列を編集しているとき、無意識の[End]キーで飛んで行きたい末尾とは列の末尾だったらしい。
あと Tabキーで次の列へ移動するとか、Enterキーで下の行へ移動するとかしたい気がするけど、違和感なく適応できるかはわからない。試してみようにもマクロから CSV/TSVモードのオンオフを知る方法がないので。
できると思うんだけど。つまり、タブストップの随時再計算をそれなりの規模の 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のコードに、自分で使うわけではないレイアウトのためのメンバーが出てくる。よくあるらしい。レイアウトは親が子のサイズを知りたがり子が親のサイズを知りたがるので、すぱっと割り切れないし決めきれない。それを解決する方法も入り組んでいて難しい。</追記>
当然の利用法として、特定の列を矩形選択で一括削除したくなったりするだろう。ダメ。削除し過ぎたり削除し足りなかったりする。なんだろう。削除の途中でタブストップの再計算が走るのだろうか。
すぐ上で
タブストップの随時再計算をそれなりの規模の CSVファイル(※ウチのPCだと富豪パッチバージョンで1MB・1万行ならもたつくけど現実的なレベルなので、それより大きいファイル)でやろうとして時間を空間で購った場合、横スクロールバーをドキュメントの最長行に対応させるためにやっていることっていうのはその一部になる。でもコストが整数2個から配列の配列と段違いに大きくなるので、CSVモードではない場合のコスト増を避けなければいけない。(折り返しモード)×(CSVモード)=4通りのコード分裂……
と書いたけど、別に配列1個でもよさそう。その場合「横スクロールバーをドキュメントの最長行に対応させるためにやっていること」とは独立したデータになるのでコード分裂もない。どういうデータを持つか。
スキャンの発生頻度はそこそこ低いのではないか。ネックは N番目の列の幅を網羅的にスキャンするときに、行の頭からスキャンして列番号をカウントしていくのでは後ろの方の列がつらいってこと。このへんはダブルクォーテーションの解釈と連携してさらなる補助データを用意することも考えられる。まあ、考えるだけで書かないんだけど……。
最終更新: 2014-11-17T11:19+0900
結局プログラム用なんだよね 漢字も強調できるけど、山田、太郎 と登録しても山田太郎はマークしない 山田商店も山田学校も全部別に登録しなきゃいけない単語区切りをどう扱うかは、言語によって違う問題だから、 拡張子によって動作を変えれるとうれしい。
他との整合性がどうなるかは知らないけど強調キーワードの単語判定は sakura_core/view/colors/CColor_KeywordSet.cpp のモジュール内インライン関数(とそこから呼ばれる sakura_core/charset/charcode.h の IS_KEYWORD_CHAR)がやってるから、その判定にテーブルを使うようにして外部から与えられるようにすればタイプ別対応も不可能ではないよ、と書き込もうとしたけど、今は CWordParse::SearchNextWordPosition4KW に文字の分類と単語の切れ目検索を任せているのだった。たしか前に、できないことができるようになったという一事をもって満足してしまうのでこういう丁寧な仕事は俺には無理だなあとかここに書いてた気がするが、それだろうか。影響の見極めが面倒ですね。しがらみとか、調整とか、最も苦手なことだ。ぼっちメンタリティがコードの構成に影響するんだろうか。
最終更新: 2013-07-14T00:41+0900
静的変数の初期化とマルチスレッド安全性。C++11より前と VCに関してはお寒い状況らしいが、そもそものコードが素朴なものだった。
もともとマルチスレッド対応ではないので静的変数で実装するのは大差ないとして(マルチスレッド対応コードを自分自身で書く機会を放棄することにはなる?)、唯一性の保証を TSingleton利用者に期待するというなら TSingletonはただグローバルアクセサを定義する手段になってしまう。この assertで従来通りの保証ができるかなと思ったんだけど
TSingleton(){ assert(this == static_cast<TSingleton<T>*>(T::getInstance())); }
static変数の初期化にロックがかかってたらこれはデッドロックを引き起こすんでない?ロックせず素通りしてしまうと未初期化オブジェクトを使用してしまうおそれがあるよね? C++11に対応した gccはどうやってるんだろ。
パッチの目的はデストラクタが呼ばれるようになることにあるらしいが、一方で、任意のタイミングでオブジェクトを破棄することはできなくなる。それはシングルトンオブジェクトにはそぐわない扱いかもしれないけど。
本当に stackoverflowのコードを利用するなら継承をやめたらいい。そうすれば TSingletonの利用者などというものは存在せずシングルトンクラスの実装者しかいなくなる。実装を提供するだけならマクロでもできるし、あえて継承にする理由は一元的にインスタンス作成を補足することではないかと。偽りの名前を与えられた中途半端な実装を別ファイルに隔離するのはどうかと思う。
3回ぐらいコメントの下書きをしてるけどぐるぐるしてまとまらなくてこの日記になる。
インスタンス変数を関数内staticではなくクラス内staticにするとアドレスを取得するのに getInstanceを呼ぶ必要がなくなって、getInstanceとコンストラクタ呼び出しがネストしなくなってロックの可能性(そんなものが実在するかは gccがないので知らない)が消える、と思ったんだけど、すべてのシングルトンオブジェクトの初期化がプログラム起動時に走ってしまいそうだ。これを避けるとインスタンスをポインタで保持することになって、これは現在のコードだ。インスタンスを関数内staticで保持したまま、getInstanceでそのアドレスだけをクラス内static変数にコピーする、とかいうのはトリッキーなわりに利がなくて目的を見失ってる感。
クラスで実装するシングルトンってなんのためにあるんだろうね。アプリケーションクラスだけがシングルトンでそれ以外はそのメンバでいいやん。Javaとか C#の Main関数みたいな居心地の悪さがあるかしらんけどさ。
同じスレッドであれば同じCRITICAL_SECTIONを引数にして何度でも EnterCriticalSectionできるらしい(同じ回数の LeaveCriticalSectionが必要)。
各スレッドはクリティカルセクションの所有権を取得した後は、自らの実行をブロックすることなく、EnterCriticalSection または TryEnterCriticalSection 関数を追加で呼び出すことができます。この結果、スレッドが既に自ら所有しているクリティカルセクションを待機しようとしてデッドロックに陥ることを防止できます。
というわけで、コンパイラが静的変数の初期化を実際どう実装するかは知らんけど、あまり気にせず上の方に書いた assertを使っていいんじゃないかな。
イベントを使ったのはこのとき(20130416)が初めてで、クリティカルセクションはまだ使ったことがないのでした。たぶんその存在はペゾルドさんの本で読んだんだろうなあ。9年前。
最終更新: 2013-06-11T17:42+0900
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による描画が食い違ってる)。
自分でもやってるけど目に見えて遅くなるから@2013-05-26常用はしてない。関連(古い順)>20110423、20120201(参考画像豊富)、20120205、20120707、20121018。DirectWriteをハンコにして得られるメリットは縦方向のアンチエイリアスがかけられることかなあ。GDI+ClearTypeでも横方向のサブピクセルレンダリングはやってるから、フォントと OS次第ではそれ以外に差が出ない。サクラエディタではサブピクセル単位での字間調整に意味がないという個別の事情もあるし。
フォントは相変わらず Consolas+Meiryoなんだけど、日本語にきちんと全角幅が割り当てられてるのが不思議。
曲線を観察するに縦方向のアンチエイリアスがかかってない気がする。それでは Consolas+Meiryo+ClearTypeに比して DirectWriteのアドバンテージがない。
早々に気にすることではないかも知れませんが、この二つの適切な呼び分けは難しくないですか?
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);
あえて無視する(無視させる)理由についても、想像がつかないもので、知っておきたいと思うのですが、教えてもらえますか?
デジャヴを感じるコメントの流れだ。
>DirectWriteで描画したい · Issue #262 · vim-jp/issues · GitHub
そんで、すぐ上の疑問に関して。たぶん novice123さんはここまで読んでいない(手が回っていない)。
koron commented 4 months ago
最新版ではGetObject()を使ってHFONTからLOGFONTを取得することで、必要なときに DWriteContext_SetFont() するように変更しました。合わせて太字と斜体の有無は、DrawTextの引数ではなくフォントの情報を利用するように変更しました。
しっかし、個別問題に対処するパッチがいくつか見られるだけでさっぱりコードの全貌が追いかけられん。リンク切れも多すぎる。
ピクセルジオメトリはダイアログで設定するものでも、できるものでもないと思う。マルチモニタ。デバッグモード?
あんまり 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で領域を指定して塗りつぶせるブラシは結果がピクセル単位で予想できる。
COMのところの2、3項目しか読んでなかったけど、常識レベルの基礎が身につく良いシリーズだったのだね。DirectWriteの APIドキュメントを読んでると出てくる DIPについても書いてあった。
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 を呼び出す必要があります。詳細については、「オブジェクトの有効期間を管理する」を参照してください。
あと、キャストせずに thisを代入して返してるけど、どういうキャストの場合に thisが変化するか熟知してない自分には不安なコード。
「QueryInterface キャスト」で検索した。そうそう、こういう懸念、
既に示したQueryInterfaceの問題点は、どのようなインターフェースが渡されてもオブジェクトのアドレスをそのまま返した点です。 このとき、適切なインターフェースでオブジェクトをキャストしていれば、 インターフェースのvptrは適切なvtblを識別するようになります。
サクラエディタで実装する、IDWriteTextRendererを継承した GdiTextRendererに真っ当な QueryInterfaceや、InterlockedIncrement/InterlockedDecrementを使った AddRef/Releaseが必要かといえばいらないんだろうけど(だから動いてる)、ざる実装でも構いはしないんだけど、一見それっぽいのはミスリーディングでよろしくない(問題があるとして、ね)。
寝る前の3時間でやっつけた!(つもりになってるだけで返り討ち……とか)。
どういう思考をたどったのか、地震(※震源は前住んでたとこだ)に揺れる風呂場で気がついた。まだバージョン管理されていない新ファイル3つがパッチに含まれていなかった。サイズが半分になってるのは小さすぎると思ったんだ。以前指摘されたのと同じミス。
https://sourceforge.net/p/sakura-editor/patchunicode/482/?limit=20#c9f7
無意味な(そう見える@2013-05-27)関数分割(20130501p01.02.01)によって
という問題に対処した点は汲んでもらえなかったようだ。Cインターフェイスという名の、DWriteContextの opaqueポインタ読み方(ハンドル)を操作する限られた関数群をとっぱらって、DWriteContextクラス自体をモジュールの外へエクスポートしたことで事態はさらに悪化した。
クラスっていうのは内にも外にも過度の依存性/コンテクスト/癒着をもたらすのだなあ。どれだけ良い点、Cからの改善点を並べようと一部のクソのために C++を忌避するという選択は理解できる。
あ、エクスポートにあたって private化はされてました。トラップは対外的には隠されていたし、呼び出し階層が明確になっていた。でも DirectWrite/DirectX系の定数をモジュールの外から隠せてない。レンダリングパラメータを int, floatと変換関数のセットで再定義してる意味がない。
チケットが pendingされてしまいました。遅いのは vim-jpが DirectWriteをオプション扱いにする理由として挙げていてわかっていたことだし、描画方法と描画先を両方とも GDIから Direct2Dに完全移行できれば改善が期待できるし、遅くても試したい人はいっぱいいたと思うのになあ。
互換インターフェイスを C++で定義するならヘッダに書く内容は DWriteContextそのままではなくこんなんになると思う。
class DirectWrite { public: static DirectWrite* Init(); DrawText(); SetFont(); SetRenderingParam(); ... //< Initと対になる後片付け関数。 private: DirectWrite(); // 呼ばせない。代わりに Init(). //< たぶんコピーコンストラクタも禁止しないといけない。 //< ポインタメンバがあるので代入演算子も禁止しないといけない。 struct DataMember; DataMember* m; };
ポリモーフィズムが必要なわけでないしファクトリメソッドの代わりにコンストラクタでもいいのかも。それでもこっそりメンバの追加はできるだろうし。
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の比較をしていた。手を抜きすぎだとは思う。
読み方 オゥペイクだってさ。オパックだと思ってたよ。思い込みは恐いよ。
最終更新: 2013-05-02T16:45+0900
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等対応)が適用されたら、文字列の色分けを正規表現キーワードから組み込みのものにまた任せることができるし、それで解決すると思う。ただし、正規表現リテラルの中の /* は解決しない。
834 :名無しさん@お腹いっぱい。:2013/05/02(木) 05:53:25.17 ID:8gLFxXSe0 正規表現リテラルとか、htmlやphpみたいな文脈による言語変更とかを考えだすと限界があるんだよな・・・ vimのシンタックスエンジンを組み込みたい。あれかなり強力だし
どんなのだろうと検索したら見つかった。>syntax - vimdoc-ja< 難しすぎるでしょ。特定のキーワード(containとか contained)がフラットな記述に構造を与えてて余計に難しいってのもあるけど。
最終更新: 2013-04-15T03:23+0900
どうせ Rubyマクロはコマンドの先頭を小文字にする必要があって他のマクロと同じように書けないのだし、使われてないからこれまでに書かれたマクロ資産との互換性を図る必要もないし、例外とリークの元になり実行前の負荷も発生させる SCRIPTITEM_GLOBALMEMBERSフラグを落とすのがいいと思う。ScriptEngine名が "RubyScript." で始まるときとかに限って。
SCRIPTITEM_GLOBALMEMBERSフラグを落とすと $Editor.insTextだけのマクロは何十回でも問題なく実行できてるけど、それだけで安心してよいものか。Editorと書いたときのように(数字で始まるマクロコマンドを呼んだときも?)初回で確実に落ちるというだけならいいけど、何回も実行してるうちに運が悪ければ落ちるというのがあれば
最終更新: 2012-10-24T00:56+0900
矩形の選択中の未選択状態でコピーをすると矩形のままになる - ID: 3578282
IsBoxSelectingは IsTextSelectingに含まれる概念だと思われるから IsTextSelecting(0幅選択を含む選択状態)と IsTextSelected(1文字以上選択されている)の2つだけで判定条件を書きたい。0文字選択→選択キャンセル&移動。1文字以上選択→選択キャンセル。実際の処理は共通でいけるはず。
IsTextSelectingは BoxSelectが必ずロックを伴うことを当てにして今のように書かれてる気がするので追い追い修正が必要になりそう。>20090923p01
でも黙っとく。実際に正しく動くか検証もしてないし。
最終更新: 2012-10-02T16:53+0900
選択された文字列かファイルの全体を 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-10T23:22+0900
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-01T14:27+0900
というわけで、CEditView::IsSearchStringからコピペコードを廃すると CColor_Foundが知りたがっている何番目の単語がマッチしたのかを知るためにさらなる検索が必要になるにもかかわらず、CEditView::IsSearchStringという変態のために仕様を決める必要は全然ない気がしてきた。仕様ってのはこれの→「sakura-unicode:1830」「複数単語検索のブックマーク・Grep対応 - ID: 3539115」。CEditView::IsSearchStringには消えてもらって CColor_Foundと CSearchAgent::SearchWordの組み合わせで何とかすべき問題ではないかと。SearchWordだと検索結果が改行をまたぐようになってもそのままで適応する利点もある(CColor_Foundと CColorStrategyの方には対処が必要だが)。
♭ もかスクリーンショット拝見しました。ExtTextOutのETO_OPAQUEようするに"背景を塗りつぶす"に相当する処..
♭ ds14050背景画像を指定したスクリーンショットもとってみました。確かに仰るとおりでした。(novice123さんが背景画像を指..