最終更新: 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の方には対処が必要だが)。
最終更新: 2012-12-05T05:04+0900
前に自分が引っかかったのと同根。「タブやタイトルバーに変更フラグが表示されるように修正した。フラグ自体は立ってたけど描画が行われていなかったというミス。(20100620p01)」
変更フラグを管理してるのは CDocEditor。ドキュメントを変更した関数が SetModified(bool modified, bool redraw)メソッドでこれに通知するが、更新フラグが立ったとき(落ちたとき)に描画が抑制されていると、以後ドキュメントの変更をトリガにしたキャプションの変更機会を失う。キャプションの内容は変数展開、条件分岐を利用して高度にカスタマイズが可能で、キャプションの更新を必要最低限にケチるのは理に適ってる。ではどうするか。描画の抑制を描画の遅延ととらえて、抑制中に加えられた描画に影響する変更を記録して、後でまとめて描画する? Webブラウザが JavaScriptに対して行ってるように? それは大変。マクロ由来のコマンドを描画を抑制して実行してるのは CMacro。かといって CMacroがコマンド呼び出しの前後で描画を適切に維持するのは出過ぎた行為。さっき「キャプションの更新を必要最低限にケチるのは理に適ってる」と書いたけど、描画の抑制をやめるのが一番簡単。だって Charマクロで InsTextのような問題が起きないのは、Charマクロの実体関数が redrawフラグを受け取らないで常に描画を行ってるせいだから。
<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; }
気付くのが遅すぎるけど、インデントを深くしたいときには意識的にタブキーとスペースキーを使い分けないといけなかった。そういうのを気にせずワンボタンで済ませられたらなお良かった。
最終更新: 2013-04-02T03:37+0900
348 :名無しさん@お腹いっぱい。:2012/05/15(火) 20:41:18.84 ID:F7CbiPBA0
正規表現検索でグループ化してる部分を全体一致とは別の色で表示するにはどうすればいいのでしょうか?
こうですか? subpattern_highlight.patch(7.7KiB)
なんでこれでいけると思ったのかが不思議なほどバグってた。スクリーンショットを撮ろうとしても、うまくいくケースを見つける方が難しい。修正した。
subpattern_highlight.rev2.patch(7.8KiB)
再帰パターンとか繰り返し(量指定子)付きグループをうまく色分けすることはできないので、あんまり実用性はない。
※ベースラインから下が切れてるのはサクラエディタのせいではありません。
最終更新: 2012-02-29T12:47+0900
と思わないでもないけど、Rubyマクロにはエディタ側に色々と問題があるのがわかってるので、大枠に沿ったまま書き直してみた。WshScriptExecを使ってみたかっただけ。WshScriptExecを得る方法は WScript.Shell.Execだけなんだけど、これを実行すると必ずコンソールが表示されるのがうまくない。こっそり実行して処理結果だけ欲しいのに。
# coding: windows-31j $/ = "\r\n" $stdout.sync = true # putsした内容をすぐ flushして .jsが受け取れるように。 puts("[size = % 4d]" % $_.chomp.size) while gets
var RubyBin = "ruby"; var RubyScr = "script.rb"; var RubyExec = new ActiveXObject("WScript.Shell").Exec("\""+ RubyBin +"\" \""+ RubyScr +"\""); RubyExec.StdErr.Close(); /* (折り返し行でない)改行単位でのカーソル移動が「GoLineTop」しかなさそうなので、これを基にしてカーソル移動を行う。 ExpandParameterで $yの変化を監視する手もあるにはある。 */ // 開始地点(最終行)へ移動。 Editor.GoFileEnd(); Editor.GoLineTop(9); if (! Editor.GetLineStr(0)) { Editor.Up(); // skip [EOF] only line. } // 下から上へ一行ずつ処理する。 for (var linenum = Editor.GetLineCount(0); 1 <= linenum; --linenum) { if (RubyExec.Status != 0) { // if not running break; // 継続は無意味。 } RubyExec.StdIn.WriteLine(Editor.GetLineStr(0).replace(/(?:\r\n?|\n)$/, "")); // カーソル行の文字列をRubyに送る Editor.InsText(RubyExec.StdOut.ReadLine()); //カーソルを上の行の先頭へ移動 Editor.GoLineTop(9); Editor.Up(); } RubyExec.StdIn.Close(); RubyExec.StdOut.Close(); RubyExec.Terminate();
最終更新: 2011-12-14T19:35+0900
ヒアドキュメントといえば前にも書いて(そんで忘れて)たが、
print <<HEAD, <<BODY HEAD BODY
みたいなのをどうするのかってことだ。<<HEAD...HEADの中に BODYだけの行が来ないことを期待しつつ、一番後ろの <<BODY...BODYだけを対象にするとか?でも <<HEADが来た時点でコメントモードが始まってしまうな。終了パターンで $+ (最後のキャプチャ内容)が使えたらいいのか?そもそもこの姑息な手でいくのか?それに開始パターンで <<HEADから、<<BODYをキャプチャしつつ、改行までを食うとしても、こういうスクリプトを想定してしまうわけだ。正規表現を使った簡易ハイライトだというのに。
p "" << <<"HEAD" <<"NECK" << <<"BODY" <head> HEAD <body> BODY #=> "<head>\nNECK<body>\n"
* 繰り返し付きグループの色分けはたぶん最後のマッチだけが対象になると思う。
最終更新: 2011-08-21T01:48+0900
(?imx-imx) 孤立オプション i: 大文字小文字照合 m: 複数行 x: 拡張形式 (?imx-imx:式) 式オプション
補記 1. 文法依存オプション + ONIG_SYNTAX_RUBY (?m): 終止符記号(.)は改行と照合成功 + ONIG_SYNTAX_PERL と ONIG_SYNTAX_JAVA (?s): 終止符記号(.)は改行と照合成功 (?m): ^ は改行の直後に照合する、$ は改行の直前に照合する
サクラエディタが自動で mフラグをくっつけて m/pattern/km みたいなのを正規表現ライブラリに渡すので油断していた。インラインでフラグを変更する方法がある。
ところで、mフラグは実装間で意味に一貫性が無く、また鬼車のフラグも間違いを誘うような名前をしているので注意が必要。みんな ECMAScriptに倣え!
+ 孤立オプションの有効範囲は、その孤立オプションを含んでいる式集合の終わりまでである 例. (?:(?i)a|b) は (?:(?i:a|b)) と解釈される、(?:(?i:a)|b)ではない
フラグの意味に加えて、その有効範囲の面倒くさいことよ。これに対処するくらいなら他の方法を考えるレベル。Onigmoは PCREみたいに、パターンのコンパイル時オプションとして「改行」を意味する文字を指定することに対応してくれないのかな。>NEWLINE CONVENTIONS(pcre.txt)
mフラグを OFFにする目的は何かといえば、JavaScriptと bregonigにおいては $ と ^ が改行前後にマッチしないように、Rubyにおいては . が改行にマッチしないように、ということだ。
「$ と ^ が改行前後にマッチしないように」は言い換えると「^ と $ が文書の頭と末尾にだけマッチするように」ということだが、サクラエディタにおいては従来、文書の先頭・末尾と各行文字列の先頭・末尾は区別できず、各行文字列末尾と改行直前の区別は曖昧だった。であれば、この問題は複数行検索が実現するときまで先送りしても差し支えないだろう。(ないよね?)
最終更新: 2017-09-20T01:01+0900
新たなバグもなく、すっかり終わってしまったプロジェクト。やり残しは、従来の、状態を持たない正規表現キーワードを色分けストラテジの一つとして移植すること。コメントや URLの色分けなんかと同じで、従来の正規表現キーワードも SHJS相当の正規表現キーワードと共存できる。そのために、協働するために余計な面倒を抱え込んだんだし。
あと、rkw2フォルダやファイルに隠し属性がついてたら読み込まない、とか。
スペース(0x20)が C_SPACEである一方、HT(0x9)/CR(0xD)/LF(0xA)が C_WHITE(その他の空白)であるせいで、タブが改行と同じ分類なせいで、一行コメントがタブインデントで終了してしまっていた。
HTML.rkw2を自作しようとしてすごく面倒くさいというか、色分け方式の能力不足を感じた。
"move" して "exit" したときに、move元の Stateに戻るだけではなくその中の moveした Patternそのものに戻り、その次のパターンから色分けを再開しなければいけないのではないか。
それはちょっと面倒だったので、"move" と "exit" が同時に指定されていたときは "move"先から exitしてきたときに "exit" が発動するようにした。サブルーチン的に使える。20170919.patch
そんなこんなで HTMLと CSSと JavaScriptの色分けを同時にするどころではなかった。HTML.rkw2。HTMLと CSSと JavaScriptが混在するときでも通用する色指定とは。
最終更新: 2013-11-24T02:49+0900
GreenPad, Alphaは復元されない。復元する派にもいろいろある。
選択状態の復元ではない。キャレットの直前/直後の文字の削除を UNDOしても選択状態になる。
これ理想。(@2013-11-23 2.1.6から 2.2現在まで、矩形選択後文字入力をアンドゥすると矩形で再現すべき選択が線形の選択になってる。これは理想ではない)
惜しい。キャレットの位置が復元されないということは、選択範囲を延長できる方向が問答無用に決まってしまうということ。
最初はメモ帳の動作をまねて CDeleteOpeの UNDOと CInsertOpeの REDOに文字列選択処理を付け加えたらいいかと思ったが、メモ帳と違い挿入や削除の連続した操作がひとまとめにされないのと UNDOバッファが無制限なせいで、連続する一文字削除(入力)操作の UNDO(REDO)がすごくくどい。CDeleteOpeと CInsertOpeの直前に CSelectOpe(新設するクラス)を挿入するのが Meryと同じ挙動になって使いやすそう。(前にも Meryが選択範囲を復元してくれるのが良いと書いた>20091117)
とりあえずこんな感じ。>undo_selectarea.rev1.patch(4.5KiB, 2011-06-11, trunk2@1923ベース)
view/CEditViewへの依存を COpe.cppから view/CEditView_Command_New.cppへ移した。>undo_selectarea.rev2.patch(4.2KiB, 2011-06-12, trunk2@1923ベース)
COpe派生クラスの実装と再生は同じ場所で行ったほうがいいような気がするが確たる考えはない*。view/CEditViewと COpeは COpeの機能から考えたら必然的に仲良しさんだから依存関係なんて気にする必要ないぜ、というなごみんの声も聞こえたが現状はそうでもないのでためらう。今 COpe属はただの構造体として扱われてるので、それに従ってロジックをコンストラクタの中から呼び出し側に移した。呼び出しごとにコピペしてね。
削除し忘れたけど「CSelectOpeがあれば CMoveCaretOpeはいらないかもね。」というコメントは正しくない。CSelectOpeは今のところキャレット位置を保存せず、その場所には適当に選択終点を代入してる。Ctrl+Aで全選択したときにキャレットが動かないので、選択始点・終点とは別にキャレット位置を覚えておかなければいけないが、今はそうしてないので CMoveCaretOpeが必要。
不要にした。>undo_selectarea.rev3.patch(4.9KiB, 2011-06-12, trunk2@1923ベース)
復元された選択範囲が正しくない。文字がない部分に選択範囲を広げられないためだ。それは、COpe属がすべてロジック単位(wchar_t列に対するインデックス)で情報を保存してるからで、その理由はたぶん、折り返し方法が変わっても対応できるようにだろう。TeraPadの readmeか何かで折り返しを変更するとアンドゥ情報がリセットされると読んだことからの類推。どうしようか。選択範囲なんてクリティカルな情報じゃないから、矩形選択時はレイアウト座標で情報を持っておくことにすれば大体はうまくいく、ってことで済ませていいんじゃないだろうか。
CLogicRangeや CLayoutRangeはコピーコンストラクタがあるから共用体のメンバになれない(C++98では)。CLogicRangeを CLayoutRangeに置き換えただけのコピペクラスを作るのか……。上のパラグラフで書いたことだけど、どっちを選んでも一長一短なのだし、矩形選択だからっていつでも選択終点がフリーカーソル状態ってわけでもない。できるだけ余計なことはしない方向で(やめとこ)。でもでも。
選択始点の型がどうして CLayoutPointでなく CLayoutRangeなのか。答えがここにある。
矩形選択して選択した全ての行に文字を挿入するということをよくする。ABDと入力・挿入していって、間違えた、Ctrl+Z、Cと操作したいのだけど、Ctrl+Zした時点で矩形選択が解除されてるのでその後の Cはキャレットのある行にしか挿入されない。残念だ。
「残念だ」と
矩形選択挿入/インデントを UNDO/REDOしたときにも選択状態を復元する>undo_selectarea.rev5.patch(13.1KiB, 2013-10-18, trunk2@1923ベース)
テストはしていない(キリッ でも自分用の sakuraW.exeには取り込んだので今日から使用中。
「呼び出しごとにコピペしてね」とは上の方で書いたが、CSelectOpe(ロジック単位の方)を作成するヘルパー関数をやっぱり用意した方がいいかもしれない。座標変換が煩雑なので。CEditViewに依存するので COpe.{h|cpp}に置くことはできないが、CViewCommander_New.cppと CEditView_Command_New.cppから利用したい。どこに置けるだろう。CViewCommander_New.cppには Command_INDENTがある。CEditView_Command_New.cppには CEditView::DeleteDataと CEditView::InsertData_CEditViewがある。Command_INDENTから呼び出される CEditView::InsertData_CEditViewで選択範囲の記録ができれば、ヘルパー関数の設置と利用は CEditView_Command_New.cppだけで完結する。それができないのは CEditView::InsertData_CEditViewがお節介にも選択範囲内のテキストを削除してくれるから、インデントを実現するために Command_INDENTが選択範囲をクリアしたり再設定したり小細工を弄する必要があって、Command_INDENT内でしか選択範囲を記録できないからだ。飛躍。エディタ(ドキュメント+ビュー)はプリミティブな操作と情報(検索、置換、選択、キャレット、ステータスバー、ダイアログなどなど)だけを提供して、各種コマンド(インデントとか検索とか)はそれらの操作を組み合わせるスクリプト(カスタマイズしやすい。オプションや好みを反映しやすい。依存されない(されにくい))であってほしかった。という感想は、CEditView::InsertData_CEditViewのようなフルスペックで融通の利かない大きな関数を、一部の機能を無理矢理引き算して利用するような状況から生まれてくる。
UNDO/REDOコマンド関数が Command_CANCEL_MODEを呼び出す部分に書いたコメント
Command_CANCEL_MODE()は主に範囲選択のクリアと選択ロックの解除を行う。 範囲選択のクリア(再描画あり)を行うと CSelect(L)Opeで範囲選択を復元したときにちらつく。 かといって、CSelect(L)Opeが含まれていないときは従来通りクリアを行いたい。 どうするか。再描画なしでクリアする。方法は Command_CANCEL_MODEに redrawフラグを渡すこともできるが Command_CANCEL_MODEの中身をコピペしてフラグだけ書き換えることにする。
範囲選択を解除する必要があるのは挿入/削除操作(CInsertOpe/CDeleteOpe)の都合かも知れない。それらの Opeの再生部分に Command_CANCEL_MODEを移動するのが3番目の方法(選択のキャンセルに伴ってキャレットが移動させられないか注意)。そもそも(出た!)、Viewに挿入/削除を行う範囲を指示するパラメータが、独立した CViewSelectなり CLayoutRangeのインスタンスではなく、ユーザーから可視の選択範囲を意味する Viewのメンバ変数でもあるから、描画のフラグをオンオフしながら選択範囲をあれこれ操作しなければいけないってのが面倒の根本。Viewにはユーザーから呼ばれるものとしてそういうメンバ変数と結びついた関数があってもいいが(※)、その関数が利用するものとしてもう少し粒度の細かい汎用性のある関数が欲しい。※良くない。そういう、メンバと結びついたコンテクストフルな関数をコマンダーに置いて、汎用性のある関数を Viewに置こうという方向で分離が進んでいたのかもしれない。
COpeで一番に改善が必要なのはメモリ確保の戦略だという気もする。1単位の UNDO/REDOのためにひとつの COpeBlkとひとつ以上(4以下くらい。CSelect(L)Opeでサンドイッチすると+2)の C*Opeのインスタンスが newされるという現実をなんとか。
COpe派生クラス(CInsertOpe/CDeleteOpeとか)の再生の前後でキャレット位置を復元するのは COpe共通のタスクとして実装されている。前後の選択範囲の復元も COpe共通のタスクにしてしまってよいかも。そうすると選択範囲のクリア、範囲の設定、テキストの挿入/削除、範囲のクリアという CInsertOpe/CDeleteOpeの手順と CSelect(L)Opeサンドイッチがひとまとめになって、空間効率も処理手順も無駄がなくなる。派生クラスの種類が減ってサイズが同じになるならメモリ戦略の実体も一本化できそう(まあ、やらないんだけど)。
そういうのはまとめて CViewSelectのコピーコンストラクタと代入演算子の仕事ではなかろうか。できるかな。そして、もしそういう操作が Viewの外に公開されてるなら CViewSelectのインスタンスを通して Viewの選択状態を変更できる setterの存在が期待されるわけだ(Viewのメンバの CViewSelectのメンバの m_sSelectを直接書き換えるとか主権侵害言語道断)。
* @2013-10-18 COpeに本物のポリモーフィズムを導入するとかいうこと。そうすると COpeの派生クラス(CSelectOpeとか)の新規導入が UNDO/REDOの実行者から不可知のまま行えるようになる。