.NETプラグイン開発 テキスト操作編

提供:MeryWiki
2016年5月27日 (金) 21:45時点におけるKs (トーク | 投稿記録)による版 (ページの作成:「= 概要 = 基本的なテキスト操作、およびプラグインからの操作の考え方です。 = テキスト操作 = プラグインでやる意味は非常...」)
(差分) ← 古い版 | 最新版 (差分) | 新しい版 → (差分)
ナビゲーションに移動 検索に移動

概要

基本的なテキスト操作、およびプラグインからの操作の考え方です。

テキスト操作

プラグインでやる意味は非常に薄いのですが、マクロと同じことをやってみます。
Mery に標準で同梱されている「昇順で並べ替え」をプラグインで実装します。

OnCommand に以下のコードを貼り付けて、ビルドし、Mery で実行します。

// ①コマンドが実行されたエディタのハンドルから、エディタオブジェクトを生成
var editor = new Editor(hWnd);

// ②エディタでアクティブなドキュメントからテキストを取得
var text = editor.GetText();

// ③テキストを行単位に分割
var array = text.Split(new char[] { '\n' });

// ④行を昇順ソート
Array.Sort(array, StringComparer.CurrentCulture);

// ⑤ソートした結果をマージ
text = string.Join("\n", array);

// ⑥結果をアクティブなドキュメントに対して反映
editor.SetText(text);

① は後の節で説明します。
② はマクロでの「ActiveDocument.Text」による取得と同じです。
③~⑤ はただの C# のコードですが、分割、ソート、マージと元のマクロと同じことをしているだけです。
⑥ は「ActiveDocument.Text」にテキストを設定するのと同じです。

見てわかるように、マクロ上では Text 一つのプロパティだったものが、それぞれ Set / Get で分かれています。
これは Set / Get が明確にわかるように、という意味以上に、GetText が高コスト(重い)処理であることが理由です。
内部的には「GetLine()」を行数分繰り返しています(API の制約)

プラグインといえど、気をつけないとあっという間に遅いものとなってしまうことに注意が必要です。

プラグインのアクセスの基本

// ①コマンドが実行されたエディタのハンドルから、エディタオブジェクトを生成
var editor = new Editor(hWnd);

ですが、この中で hWnd は OnCommand の引数で、自動で渡されるハンドルです。
これは「コマンドが実行された Editor のハンドル」です。

プラグインではマクロのように Editors -> Editor -> Documents -> Document のように独立したオブジェクトになっておらず、直接アクセスすることもできません。
プラグインでの基本的なアクセス単位は全て「Editor」に対してとなります。
その時の操作は常に「ActiveDocument」への操作となります。
では複数のタブがある場合に、別のタブを操作するにはどうするかと言えば、ActiveDocument の切り替え、つまり処理対象のタブをアクティブにします。
Mery.DotNetLib.Editor.SetActiveDocument() というメソッドが用意されているので、これで別のタブをアクティブにし処理をします。
元の状態に戻す必要があるならば、最初のアクティブなタブを覚えておき、最後に戻してあげる必要があります。

このように、マクロとプラグインではアクセスの単位が異なります。
マクロで用意されている機能のほとんどはプラグインでも利用可能ですが、その使用方法には違いがあることに注意してください。
(さらに、マクロで提供されていてプラグインで提供されていない機能もあるので、プラグインも万能ではありません)

タブ操作

全タブへの操作のサンプルを書いてみます。

// ①コマンドが実行されたエディタのハンドルから、エディタオブジェクトを生成
var editor = new Editor(hWnd);

// ②現在アクティブなタブのインデックス番号を取得
int activedIndex = editor.GetActiveDocumentIndex();

// ③Editor 内の全タブ数を取得
int tabCount = editor.GetDocumentCount();

int total = 0;
for (int i = 0; i < tabCount; i++) {
    // ④処理対象のタブをアクティブ化
    editor.SetActiveDocument(i);

    // ⑤タブ内の文字数を取得して、全文字数へ加算
    total += editor.GetLength(true);
}

// ⑥最初にアクティブだったタブをアクティブ化
editor.SetActiveDocument(activedIndex);

// ⑦結果の表示
MessageBox.Show("全タブの文字数:" + total);

複数のタブを開いて実行してみてください。
全てのタブの合計文字数が表示されるはずです。

③ で N 個のタブを開いているのがわかります。
インデックスは 0 始まりで N-1 までなので、それに合わせてループし ④ でアクティブ化します。
⑤ でタブ内の全文字数を取得し、合算しています。
マクロであれば、ActiveDocument.Text.length と表現するため、editor.GetText().Length としたくなりますが、上で書いているように GetText() は重い処理となるため専用の GetLength() を使用しています。
(もし興味があれば、非常にでかいテキストに対し実行して、違いを確認してみてください。10 万行を超えてくると違いが顕著になります)

実行してみて気づくことがあります。
それはパタパタとタブの切り替えが見えてしまうことです。
プラグインにおける弱点の 1 つがこれで、マクロであればアクティブ化が必要ないためこのようなことがないのですが、プラグインでは常にアクティブ化を意識する必要があり、結果切り替えが見えてしまいます。
マクロにおける window.Redraw に該当する Editor.SetRedraw() が存在するため、①と②の間に editor.SetRedraw(false); を、⑥と⑦の間に editor.SetRedraw(true); を入れてみてください。
実行してみると、パタパタしなくなることがわかります。

ただし注意点として、マクロの Redraw は true にしなくとも勝手に再描画されるようになりますが、プラグインの場合はなりません。
意図的に true にしてあげないと描画が止まり、Mery が固まったように見えてしまいます。
万全を期すならば、try - finally で確実に描画を再開できるようにする必要があります。

プラグインはマクロより危険な状態になりやすいため、扱いには気をつけましょう。

スポンサーリンク