1 件の投稿を表示中 (合計 10 個)
  • 作成者
    投稿
  • #7843
    Delフサ
    参加者

    こんにちは。

    EmEditorPluginを作っています。

    疑問点があり、高速化できないので
    やり方を教えてほしい。と、いう内容なのですが
    現状用意されているAPIでは
    実現できないような気もしますので
    APIの要望にもなります。

    某掲示板で
    「ステータスバーに現在の入力テキストの
     バイト数を表示するプラグインがほしい」
    という意見があがりまして作ってみました。

    テキスト選択している場合は
    その選択した部分のバイト数/半角全角文字数/行数
    選択していない場合はキャレット前方のテキストについて
    バイト数/半角全角文字数/行数

    を、全テキストと併記するようなプラグインを作ってみました。

    テキスト編集時に随時更新するように作ってみたのですが
    小さいテキストなら動作に問題はないのですが
    1万行クラス、512KB程度のテキストになってくると
    動作がかなり遅くなってきます。

    私の組み方が悪いのかと思い、何パターンものやり方を試しましたが
    現在の仕組みでは、どうやっても高速化できない気がしています。

    実装方法は次の通りです
    ◆OnEventsにて
    EVENT_CHANGE と EVENT_DOC_SEL_CHANGED で
    テキスト変更フラグを立てて

    EVENT_CARET_MOVED と EVENT_SEL_CHANGED で
    選択変更フラグを立てて、

    EVENT_IDLE にて処理を行っています。
    テキスト変更フラグが立っている場合、
    全テキストを取得して、文字数やバイト数などをカウント
    選択変更フラグが立っている場合
    テキストが選択されているかどうかを確認して
    選択テキストか、キャレット前方テキストを取得
    文字数やバイト数などカウントして

    その情報をステータスバーに出力している。

    上記実装の大枠については特に問題がないです。
    EVENT_IDLEは負荷がない時に発生するという
    わけではなく、処理が一段落したら発生しているために
    遅くても毎回処理が行われることは納得できるのですが
    低速の直接の問題は、全テキストやキャレット前方テキストを
    取得する方法が遅いことです。

    ◆全てのテキストやキャレット前方テキストを取得する場合
    試しましたのは次の方法
    A 選択範囲をバッファ
    全選択もしくはキャレット前方選択(Editor_SetSelView)してEditor_GetSelTextWする
    選択範囲を戻す
    このやり方は一番高速でしたが実用的とはいえません。
    選択位置を毎回変更して戻しているというのが
    遅さの原因かと思いますし不自然な感じがします。

    B 上と同様に
    選択範囲をバッファ
    全選択してEditor_GetSelTextWする
    選択範囲を戻す
    この時に、全選択する場合
    Editor_ExecCommand(hwnd, EEID_EDIT_SELECT_ALL)
    などを使った。
    このやり方は遅すぎました。

    C 選択範囲は変えずに
    Editor_GetLineWを用いて
    行をループして、文字列を取得した。
    文字列を毎行生成すると低速なはわかりきっているので
    毎行ごとの文字数を求めて全て加算してから
    文字列領域を確保して
    そこに対してEditor_GetLineWで位置を指定して
    毎行ごとにテキスト内容を取得したのですが
    これで、早くなるかと思ったのですが
    Aのやり方より低速になってしまいました。

    ということで、
    こういう場合、全テキスト、もしくは、キャレット前方テキスト
    を、高速に取得する方法は、どのようにしたらよいのでしょうか。

    という質問とともに、

    一発で、全テキスト や キャレット前方(および後方)テキストを
    取得できるようなAPIを作成していただきたいなと思いました。

    全テキストを取得したい場合に
    上記のやり方しかないと、動作が遅いのはつらいです。

    長くなりました。
    よろしくおねがいします。

    #7845
    snow
    参加者

    こんにちは。

    バイト数をカウントするのに、現在エディタが格納している文字列を取得(コピー)すること自体が、無駄な気がします。
    プラグインのリファレンスを見てみると、「シリアル位置」というのがドキュメント中のバイト位置を示しているのでしょうか。

    カーソル以降の全バイト数
    =最終行のシリアル位置
    +最終行のバイト数
    -カーソル行のシリアル位置
    -カーソル行のカーソル以前のバイト数

    なのかなあと、考えてみました。

    プラグインは触っていないので、的外れなことを言ってるかも知れませんし、速くなるとも限りませんが…。
    だめだったら済みません。

    #7846

    横から失礼。

    全体の文字数を取るなら GetWindowTextLength
    全体の文字列を取るなら GetWindowText
    が使えますよ。
    (8.05 32bit版のOnCommandとOnEventsに渡ってくるウィンドウハンドルについて実験済み。)

    ただし、改行文字も(おそらくその他の制御文字も)含まれます。
    試した範囲では、改行コードがCRLFのようなので一改行に付き2文字分増えます。

    #7847

    横から失礼。

    snowさんのおっしゃるカーソル位置のシリアル位置取得なら
    POINT_PTR pp;
    Editor_GetCaretPos( hwnd, POS_LOGICAL_W, &pp );
    UINT_PTR n = Editor_LogicalToSerial( hwnd, &pp );
    でいけますね。
    昔使ったことあるのに、存在をすっかり忘れてた・・・

    こちらも例によって改行文字がカウントされます。
    また、フリーカーソル時に改行の右側へカーソルが行ったときのことを若干気にした方がいいかもしれません。

    #7848
    Delフサ
    参加者

    ありがとうございます。
    バイト数だけではなく、その中の文字列を調べて
    半角/全角文字の個数を求めようと思うので
    全部のテキストがほしいと思っているのです。

    最終行のシリアル位置というのは役にたちそうですね。
    気がつきませんでした。

    #7849
    Delフサ
    参加者

    おお!
    なるほど、そういう手がありましたか。
    改行文字は、むしろほしいのでばっちりです。(取り除いてもOKですね)

    お隣のお返事ですが、
    フリーカーソルは忘れてました。
    考慮してみます。

    全テキストを取得してから、キャレット前方テキストを
    自前で抽出してみます。

    ありがとうございます。

    #7850

    よくよく考えてみたら、巨大ファイルコントローラ使って大きなファイルを部分的に読み込んでるときは、GetWindowText系はうまくいかないかもしれません。(未テスト)

    ファイルが巨大な場合(EmEditorは248 GB以上のファイルも処理できることになっている)、いかなる手段であれ全体を取得するのは無理があると思うので、一定以上大きなファイルの時は全体を取得しないようにした方がいいと思います。

    #7852
    Delフサ
    参加者

    どうやっても完全保証とはいかなさそうですね。

    ただ、1万行程度で動作が落ちるのは嫌なんですよね・・・

    10万行で、遅くなるなら、まあ支障はないかな…という感じです。

    バックグラウンドで動かせて
    動作が遅くなったら停止して
    動作に余裕がでたら計算するとか
    そういう事が簡単になればいいんですけどね。

    #7857
    snow
    参加者

    タイマーイベントは使えないんでしょうか。
    と、またプラグインについて良く知らないのに、口を出してみます。

    #7859
    Delフサ
    参加者

    スレッドとか、上手に使えればいいのですが
    普通のプログラミングでもスレッド化するのは難しい面が多いのですが
    プラグイン中でそのようにするのは更に難しい感じがあるのですよ。

1 件の投稿を表示中 (合計 10 個)
  • このトピックに返信するにはログインしてください。