「.NETプラグイン開発 テキスト操作編」の版間の差分

提供: MeryWiki
ナビゲーションに移動 検索に移動
MSY-07 (トーク | 投稿記録)
<source>タグを<syntaxhighlight>タグに置き換え
MSY-07 (トーク | 投稿記録)
機種依存記号を修正
9行目: 9行目:


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


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


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


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


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


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


は後の節で説明します。<br>
(1) は後の節で説明します。<br>
はマクロでの「ActiveDocument.Text」による取得と同じです。<br>
(2) はマクロでの「ActiveDocument.Text」による取得と同じです。<br>
③~⑤ はただの C# のコードですが、分割、ソート、マージと元のマクロと同じことをしているだけです。<br>
(3)~(5) はただの C# のコードですが、分割、ソート、マージと元のマクロと同じことをしているだけです。<br>
は「ActiveDocument.Text」にテキストを設定するのと同じです。<br>
(6) は「ActiveDocument.Text」にテキストを設定するのと同じです。<br>
<br>
<br>
見てわかるように、マクロ上では Text 一つのプロパティだったものが、それぞれ Set / Get で分かれています。<br>
見てわかるように、マクロ上では Text 一つのプロパティだったものが、それぞれ Set / Get で分かれています。<br>
41行目: 41行目:
== プラグインのアクセスの基本 ==
== プラグインのアクセスの基本 ==
<syntaxhighlight lang="csharp">
<syntaxhighlight lang="csharp">
// ①コマンドが実行されたエディタのハンドルから、エディタオブジェクトを生成
// (1)コマンドが実行されたエディタのハンドルから、エディタオブジェクトを生成
var editor = new Editor(hWnd);
var editor = new Editor(hWnd);
</syntaxhighlight>
</syntaxhighlight>
63行目: 63行目:


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


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


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


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


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


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


// ⑦結果の表示
// (7)結果の表示
MessageBox.Show("全タブの文字数:" + total);
MessageBox.Show("全タブの文字数:" + total);
</syntaxhighlight>
</syntaxhighlight>
91行目: 91行目:
全てのタブの合計文字数が表示されるはずです。<br>
全てのタブの合計文字数が表示されるはずです。<br>
<br>
<br>
で N 個のタブを開いているのがわかります。<br>
(3) で N 個のタブを開いているのがわかります。<br>
インデックスは 0 始まりで N-1 までなので、それに合わせてループし でアクティブ化します。<br>
インデックスは 0 始まりで N-1 までなので、それに合わせてループし (4) でアクティブ化します。<br>
でタブ内の全文字数を取得し、合算しています。<br>
(5) でタブ内の全文字数を取得し、合算しています。<br>
マクロであれば、ActiveDocument.Text.length と表現するため、editor.GetText().Length としたくなりますが、上で書いているように GetText() は重い処理となるため専用の GetLength() を使用しています。<br>
マクロであれば、ActiveDocument.Text.length と表現するため、editor.GetText().Length としたくなりますが、上で書いているように GetText() は重い処理となるため専用の GetLength() を使用しています。<br>
(もし興味があれば、非常にでかいテキストに対し実行して、違いを確認してみてください。10 万行を超えてくると違いが顕著になります)<br>
(もし興味があれば、非常にでかいテキストに対し実行して、違いを確認してみてください。10 万行を超えてくると違いが顕著になります)<br>
100行目: 100行目:
それはパタパタとタブの切り替えが見えてしまうことです。<br>
それはパタパタとタブの切り替えが見えてしまうことです。<br>
プラグインにおける弱点の 1 つがこれで、マクロであればアクティブ化が必要ないためこのようなことがないのですが、プラグインでは常にアクティブ化を意識する必要があり、結果切り替えが見えてしまいます。<br>
プラグインにおける弱点の 1 つがこれで、マクロであればアクティブ化が必要ないためこのようなことがないのですが、プラグインでは常にアクティブ化を意識する必要があり、結果切り替えが見えてしまいます。<br>
マクロにおける window.Redraw に該当する Editor.SetRedraw() が存在するため、①と②の間に editor.SetRedraw(false); を、⑥と⑦の間に editor.SetRedraw(true); を入れてみてください。<br>
マクロにおける window.Redraw に該当する Editor.SetRedraw() が存在するため、(1)と(2)の間に editor.SetRedraw(false); を、(6)と(7)の間に editor.SetRedraw(true); を入れてみてください。<br>
実行してみると、パタパタしなくなることがわかります。<br>
実行してみると、パタパタしなくなることがわかります。<br>
<br>
<br>

2023年5月17日 (水) 19:02時点における版

概要

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

テキスト操作

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

タブ操作

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

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

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

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

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

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

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

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

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

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

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

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

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

スポンサーリンク