プラグインから「タブ幅」と「論理行で表示する」の設定

  1. 「Mery」の開発、ありがとうございます。

    現在プラグインの「MeryRowCol.dll」を公開していますが機能拡張を考えています。

    1:「タブ幅」は「Mery.ini」から読み取っていますが、「Mery」を起動中にタブ幅を変更しても直ぐには反映されません。

    その対応として、「タブ幅」をプラグインから設定できる機能を使いたいです。「Mery」側は指定された数値に「タブ幅」を設定します。「プラグイン」側は設定値を記憶して、記憶したタブ幅で処理を行います。

    2:「論理行で表示する」の状態は「MEID_VIEW_LINE_NUMBERS_LOGICAL = 3111」を指定すれば、チェックの状態を取得できます。

    「論理行で表示する」のチェックとアンチェックをプラグインから行うことは可能でしょうか?該当しそうな「nCmdID」を試しましたが見つかりませんでした。

    要望の元になったのは
    1:文書によりタブ幅を変更する(変更する機会が多い)
    2:プログラムの文書では「論理行」で、それ以外では「表示行」を設定する
    という内容です。
    「MeryRowCol.dll」から両方の変更が出来れば設定手順が簡単になるのではと考えています。

    よろしくお願いします。

     |  大石剛司  |  返信
  2. こんにちは。開発お疲れさまです。

    > 1:文書によりタブ幅を変更する(変更する機会が多い)

    通常、タブ幅は全体で共通の設定ですが、プラグインからであれば、文書ごとにタブ幅を変更することができます。

    Editor_Info(FEditorHandle, MI_SET_INDENT_SIZE, 8);
    

    ただし、この方法で変更したタブ幅はその文書だけに適用される一時的な設定であり、オプション画面などの共通設定には反映されません。

    現在のタブ幅は、次のように MI_GET_INDENT_SIZE で取得できます。

    LSize := Editor_Info(FEditorHandle, MI_GET_INDENT_SIZE, 0);
    

    一度も変更されていない場合 (オプション画面の共通設定が適用されている場合) は、-1 が返ります。

    > 2:プログラムの文書では「論理行」で、それ以外では「表示行」を設定する

    MEID_VIEW_LINE_NUMBERS_LOGICAL でチェック状態の取得まではできているとのことなので、あとは Editor_ExecCommand を使えば制御できます。

    たとえば、次のような関数を用意します。

    procedure SetLineNumbersLogicalChecked(AChecked: Boolean);
    var
      LChecked: BOOL;
    begin
      LChecked := False;
      Editor_QueryStatus(FEditorHandle, MEID_VIEW_LINE_NUMBERS_LOGICAL, @LChecked);
      if AChecked <> LChecked then
        Editor_ExecCommand(FEditorHandle, MEID_VIEW_LINE_NUMBERS_LOGICAL);
    end;
    

    これで、次のように呼び出せば OK です。

    SetLineNumbersLogicalChecked(True); // [行番号を論理行で表示] をオン
    

    このようにすれば、チェック状態に関係なく、明示的にオン/オフを切り替えることができます。

     |  Kuro  |  返信
  3. >通常、タブ幅は全体で共通の設定ですが、プラグインからであれば、文書ごとにタブ幅を変更することができます。

    この機能は便利ですね。
    「MeryRowCol」のプロパティ画面を開いて、該当のタブ設定のボタンを押すだけで設定が可能になりました。

    >MEID_VIEW_LINE_NUMBERS_LOGICAL

    こちらの解説もありがとうございました。

    「MeryRowCol.dll」の更新が完了しましたが、もう少し様子を見ます。
    「四則演算」の「MeryCalc.dll」は作成中で、こちらももう少し様子を見ます。

    ありがとうございました。

     |  大石剛司  |  返信
  4. もう一点、確認したい項目があります。
    インストール版を使用した「Mery」の動作についての確認です。

    マクロの実行フォルダですがインストール後には以下になっていました。
    「C:\Users\<user>\AppData\Local\Programs\Mery\Macros\」

    プラグインからマクロ実行ファイルを保存したいのですが、上記のマクロフォルダにはファイルの保存に失敗するので、その代わりに書き込み可能な以下に保存しました。
    「C:\Users\<user>\AppData\Roaming\Mery\Macros\MeryReadOnlyGetStatus.js」

    この状態でマクロファイルを実行する時の関数
    function Editor_RunMacro(hwnd: THandle; nFlags: Cardinal;
    nDefMacroLang: Cardinal; pszMacroFile: PChar; pszText: PChar;
    pptErrorPos: PPoint): HRESULT;
    の「pszMacroFile」に 「C:\Users\<user>\AppData\Roaming\Mery\Macros\MeryReadOnlyGetStatus.js」を指定しています。

    マクロファイルは実行できていますが「Mery」をインストールしたフォルダにマクロファイルが無くても大丈夫でしょうか?

    よろしくお願いします。

     |  大石剛司  |  返信
  5. > マクロファイルは実行できていますが「Mery」をインストールしたフォルダにマクロファイルが無くても大丈夫でしょうか?

    大丈夫です。マクロ ファイルはどこに置いても使えます。

    もともと、マクロはマイ ドキュメントなど自分の好きなフォルダーに置いて使うことを想定しているので、特にインストール フォルダーに置く必要はありません。

    ただし、Mery\Macros フォルダーはちょっと特殊で、ここにあるマクロは他のマクロから include (相対パス) で読み込めます。

    なので、どのマクロからも共通で include したいマクロは、Mery\Macros フォルダーに置くと便利です。

    ちなみに、プラグインはセキュリティ上の理由から、必ず Mery\Plugins フォルダーに置く必要があります。

     |  Kuro  |  返信
  6. 解説ありがとうございました。
    プラグインにどっぷり浸かっています。

    1:シリアル位置に関しても教えて下さい。

    // シリアル位置をカーソル位置に変換
    // Editor_SerialToLogical(hwnd: THandle; nSerial: Cardinal; pptLogical: PPoint);

    nSerial に -1 を指定すると「Mery」が落ちるので、0 以下を設定しないようにしました。

    nSerial に 100000 等の全体の文字数より大きな数値を設定しても「Mery」は最終行の情報を返します。
    現状では大丈夫ですが、こちらも数値が全体のシリアル数を超えないようにしたいと思います。
    全体のシリアル数を取得する機能があれば使いたいと思いますが見つけられませんでした。

    一応以下の方法で取得はできました。
    全体の行数を取得して、最終行のシリアル位置(A)を取得します。
    最終行のテキストを取得して、テキスト長(B)を取得します。
    シリアル値は(A)+(B)で良いですか?
    // 文書行数
    LLinesAll := Editor_GetLines(hwnd, POS_LOGICAL);
    // シリアル位置
    LSirialAll := Editor_LineIndex(hwnd, TRUE, LLinesAll-1);
    // 最終行のテキストを取得
    LInfo.cch := 0;
    LInfo.byteCrLf := 0;
    LInfo.flags := FLAG_LOGICAL;
    LInfo.yLine := LLinesAll-1;
    // 最終行の文字数
    LLen := Editor_GetLine(hwnd, @LInfo, nil);
    // 最終行の情報
    showmessage(inttostr(LSirialAll) + #13#10 + inttostr(LLen));

    2:ブックマークに関しても教えて下さい。

    Mery.his には以下が設定されています。
    Mark0=0
    Mark1=2
    Mark2=13

    以下を実行するとカーソル位置に関係なく同じ数値が返ります。

    iBook1 := Editor_Info(hwnd, MI_GET_BOOKMARK_COUNT, 0);
    現在のブックマーク数(3)が取得されます

    iBook1 := Editor_Info(hwnd, MI_GET_NEXT_BOOKMARK, 0);
    返り値に(2)が取得されます

    iBook1 := Editor_Info(hwnd, MI_GET_PREV_BOOKMARK, 0);
    返り値に(-1)が取得されます

    MI_GET_NEXT_BOOKMARK と MI_GET_PREV_BOOKMARK が返す値の意味が理解できていません。

    よろしくお願いします。

     |  大石剛司  |  返信
  7. こんばんは。開発お疲れさまです。

    > 1:シリアル位置に関しても教えて下さい。

    > nSerial に -1 を指定すると「Mery」が落ちるので、0 以下を設定しないようにしました。

    -1 が渡されることは想定していませんでした。

    プラグイン開発の API はほとんど自分だけで使っていたため、想定外の値が来たときの対策まで手が回っていないのが現状です。

    とはいえ、Editor_SerialToLogical については、負の値が渡されたときに落ちないようチェックを入れておきますね。(他の関数にもチェックが足りないものがあると思うので、気づいたときに少しずつ直していきます)

    > 全体のシリアル数を取得する機能があれば使いたいと思いますが見つけられませんでした。

    全体のシリアル数 (=文字数) を取得したい場合は、Editor_GetText に 0 と nil を渡すと OK です。

    LTextLength := Editor_GetText(FEditor, 0, nil);
    

    この場合、戻り値は「全体のシリアル数 + 1」(末尾のヌル文字ぶん) になります。

    > 2:ブックマークに関しても教えて下さい。
    > MI_GET_NEXT_BOOKMARK と MI_GET_PREV_BOOKMARK が返す値の意味が理解できていません。

    MI_GET_NEXT_BOOKMARKMI_GET_PREV_BOOKMARK は、第 3 引数に「0 から始まる論理行番号」を渡すと、その行を基準に「次/前のブックマークがある行番号」を返します。

    たとえば、

    iBook1 := Editor_Info(hwnd, MI_GET_NEXT_BOOKMARK, 0);
    

    これだと、1 行目を基準に、次にブックマークがある行 (Mark1=2) が返ってきます。

    iBook1 := Editor_Info(hwnd, MI_GET_NEXT_BOOKMARK, 3);
    

    これは 3 行目を基準に、その次のブックマーク位置を返します。

    前のブックマークの場合は、

    iBook1 := Editor_Info(hwnd, MI_GET_PREV_BOOKMARK, 0);
    

    これだと、1 行目を基準にしているので「前のブックマークはない」と判断され、-1 が返ります。

    すべてのブックマークを順に取得したいときは、次のように書けます。

    var
      LLine: Integer;
    begin
      LLine := 0;
      while LLine > -1 do
      begin
        LLine := Editor_Info(FEditor, MI_GET_NEXT_BOOKMARK, LLine);
        if LLine < 0 then
          Break;
        OutputDebugString(PChar(IntToStr(LLine)));
      end;
    

    …で、書いてて気づいたのですが、今の仕様だと LLine が 0 から始まるので、1 行目にあるブックマークは取得できないですね。

    これについては、Mery 側で LLine = -1 も受け付けるように修正しておきますね。

     |  Kuro  |  返信
  8. >全体のシリアル数 (=文字数) を取得したい場合は、Editor_GetText に 0 と nil を渡すと OK です。

    ありがとうございます。早速組み込みました。

    プラグインで開発をしていますが、判らない箇所があります。

    「ウィンドウ」の「タブを有効にする」がアンチェックの場合に、複数のファイルの「書き込み禁止」の状態の取得方法です。

    判っている事は、「ウィンドウ」の「タブを有効にする」がチェックの場合は
    Editor_Info(hwnd, MI_GET_DOC_COUNT, 0)
    では、複数のファイルを開いている場合には、開いている文書の数が取得できます。

    また、「ウィンドウ」の「タブを有効にする」がアンチェックの場合は
    Editor_Info(hwnd, MI_GET_DOC_COUNT, 0)
    では、複数のファイルを開いていても「1」が返ります。

    この時に開いているファイルの個数はどのように取得するのでしょうか?
    今までは、EnumWindows で「 -Mery」のタイトルを取得していました。

    また、開いている文書数が取得出来たら、各文書の「書き込み禁止」の状態を取得したいのです。
    これまではマクロを実行させて取得していましたが、何故か取得できない場合があります。

    基本的な事が判っていないようです。

    よろしくお願いします。

     |  大石剛司  |  返信
  9. > また、「ウィンドウ」の「タブを有効にする」がアンチェックの場合は Editor_Info(hwnd, MI_GET_DOC_COUNT, 0) では、複数のファイルを開いていても「1」が返ります。

    MI_GET_DOC_COUNT は hwnd (1 つの Mery ウィンドウ) に対して開いているタブの数を返す仕組みなので、タブ機能を無効にしている場合は常に 1 が返るかたちになりますね。

    > この時に開いているファイルの個数はどのように取得するのでしょうか?
    > 今までは、EnumWindows で「 -Mery」のタイトルを取得していました。

    今のところ、開いているファイル数そのものを直接返す API は用意していないので、これまでどおり EnumWindows で「- Mery」を拾う方法でも問題ないと思います。

    丁寧にやるなら、タブの有無に関係なく ウィンドウ生成時には EVENT_CREATE_FRAME、破棄時には EVENT_CLOSE_FRAME が飛んでくるので、これらをフックして hwnd をプラグイン側で管理しておくと、現在開いているウィンドウ数を正確に追えると思います。

    > また、開いている文書数が取得出来たら、各文書の「書き込み禁止」の状態を取得したいのです。

    書き込み禁止の状態は、ドキュメント (タブ) 単位でこんな感じで取れます。

    var
      LReadOnly: Boolean;
    begin
      LReadOnly := Boolean(Editor_DocInfo(hwnd, 1, MI_GET_READ_ONLY, 0));
    

    Editor_DocInfo は「タブごとの情報」を取得する関数で、第 2 引数が「左から数えた 0 始まりのタブ番号」になります。(この例では 1 なので左から 2 番目のタブの情報を取得)

    ここの hwnd はウィンドウ単位のハンドルなので、「- Mery」から拾った hwnd に対してそのままメッセージを送っても大丈夫だと思います。

     |  Kuro  |  返信
  10. 詳細な情報ありがとうございます。

    「ウィンドウ」の「タブを有効にする」のチェック・アンチェックによる使い分けが理解できました。Enumwindows で取得したハンドルが「Mery」から設定されるハンドルと同じことを確認しました。これでスッキリとしました。ありがとうございました。

    土曜と日曜は猫関係のイベントで外出していて、戻ってきたら速攻で寝ました。今日からプログラミングに復帰です。

    よろしくお願いします。

     |  大石剛司  |  返信
  11. うまく動いたようで、安心しました。

    それと、本日リリースした Ver 3.8.0 で、MI_GET_NEXT_BOOKMARK が引数に -1 を受け取れるように修正してあります。

    var
      LLine: Integer;
    begin
      LLine := -1;
      while True do
      begin
        LLine := Editor_Info(FEditor, MI_GET_NEXT_BOOKMARK, LLine);
        if LLine < 0 then
          Break;
        OutputDebugString(PChar(IntToStr(LLine)));
      end;
    

    こんな感じで、1 行目を含めてすべてのブックマークを取得できるようになっているはずです。

    > 土曜と日曜は猫関係のイベントで外出していて、戻ってきたら速攻で寝ました。今日からプログラミングに復帰です。

    猫のイベント、いいですね😺

    こちらは Ver 3.8.0 をなんとか公開できたので、少しお休みをいただきつつ、猫とゆっくり過ごそうと思います。

     |  Kuro  |  返信
  12. >こんな感じで、1 行目を含めてすべてのブックマークを取得できるようになっているはずです。

    3.7.17 で動作確認をしていたので「2行目から後方に検索」と「2行目から前方に検索」の両方の結果を取得して対応しました。

    「[」と「]」の括弧検索ですがプラグインで実現してみました。前方向16個、後方向16個の指定が可能で、グループは31個の登録を可能にしました。設定画面がスマートではないのですが操作重視の画面になっています。

    現状では「MeryReadOnly.dll」で実装しましたが、様子を見てから括弧検索機能だけのプラグインを公開する予定です。

    よろしくお願いします。

     |  大石剛司  |  返信
  13. > 3.7.17 で動作確認をしていたので「2行目から後方に検索」と「2行目から前方に検索」の両方の結果を取得して対応しました。

    なるほど、そんな方法がありましたか。

    > 「[」と「]」の括弧検索ですがプラグインで実現してみました。前方向16個、後方向16個の指定が可能で、グループは31個の登録を可能にしました。設定画面がスマートではないのですが操作重視の画面になっています。

    そうなんですよね。対応する括弧を設定する UI を考え始めると、どうしてもスマートなかたちが思いつかなくて…。シンプル志向の Mery としては、現状はあえて表に出していない感じです。

    左括弧と右括弧をセットで指定しないといけないのも、けっこう悩ましいところですよね。

    ある程度、使用する括弧の種類を固定してしまって良ければ、括弧の組み合わせをリストで表示して、チェックボックスのオン/オフで切り替える…というかたちも考えられなくはないのですが。

    > 現状では「MeryReadOnly.dll」で実装しましたが、様子を見てから括弧検索機能だけのプラグインを公開する予定です。

    どんな設定画面になっているのか、楽しみにしています。

     |  Kuro  |  返信
  14. 複数語の検索機能のプラグインを公開しました。
    設定画面はむりやり作成した感じになってしまいました。

    https://www.bonsfm.com/merywindows/meryfindcharwin

    同時に、四則演算のプラグインも公開しました。
    マクロでの計算機能をプラグインで実現しようとトライしましたが実装できませんでした。
    また「busybox.exe」を使用する方法にもトライしましたが実装できませんでした。

    https://www.bonsfm.com/merywindows/merycalcwin

    最終的に「奥山晴彦」様が執筆されている「コンピュータ アルゴリズム辞典」の「式の評価」を使っています。
    ソースファイルの公開には奥山晴彦様は快く承諾して戴きました。ありがとうございました。

    よろしくお願いします。

     |  大石剛司  |  返信
  15. 開発お疲れ様です。

    > 複数語の検索機能のプラグインを公開しました。
    > 設定画面はむりやり作成した感じになってしまいました。

    拝見しました。なるほど、設定画面は分かりやすさ重視ですね。良い仕上がりだと思います。

    > 同時に、四則演算のプラグインも公開しました。
    > マクロでの計算機能をプラグインで実現しようとトライしましたが実装できませんでした。
    > また「busybox.exe」を使用する方法にもトライしましたが実装できませんでした。

    おおー、しっかり動作しますね。

    busybox.exe を使う方法だと別途導入が必要になるので、プラグインだけで完結する自前実装は、使う側としてはありがたいです。

    Delphi での「式の評価」の実装アルゴリズム、想像していたよりもずっとシンプルで驚きました。

    興味深い情報を共有していただき、ありがとうございました。

     |  Kuro  |  返信
スポンサーリンク