「.NETプラグイン開発 テキスト操作編」の版間の差分
(ページの作成:「= 概要 = 基本的なテキスト操作、およびプラグインからの操作の考え方です。 = テキスト操作 = プラグインでやる意味は非常...」) |
(改行の修正) |
||
(同じ利用者による、間の3版が非表示) | |||
1行目: | 1行目: | ||
= 概要 = | == 概要 == | ||
基本的なテキスト操作、およびプラグインからの操作の考え方です。 | 基本的なテキスト操作、およびプラグインからの操作の考え方です。 | ||
= テキスト操作 = | == テキスト操作 == | ||
プラグインでやる意味は非常に薄いのですが、マクロと同じことをやってみます。Mery に標準で同梱されている「昇順で並べ替え」をプラグインで実装します。 | |||
OnCommand に以下のコードを貼り付けて、ビルドし、Mery で実行します。 | OnCommand に以下のコードを貼り付けて、ビルドし、Mery で実行します。 | ||
< | <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> | ||
(1) は後の節で説明します。 | |||
(2) はマクロでの「ActiveDocument.Text」による取得と同じです。 | |||
(3)~(5) はただの C# のコードですが、分割、ソート、マージと元のマクロと同じことをしているだけです。 | |||
= プラグインのアクセスの基本 = | (6) は「ActiveDocument.Text」にテキストを設定するのと同じです。 | ||
< | |||
// | 見てわかるように、マクロ上では Text 一つのプロパティだったものが、それぞれ Set / Get で分かれています。これは Set / Get が明確にわかるように、という意味以上に、GetText が高コスト(重い)処理であることが理由です。内部的には「GetLine()」を行数分繰り返しています(API の制約)。 | ||
プラグインといえど、気をつけないとあっという間に遅いものとなってしまうことに注意が必要です。 | |||
== プラグインのアクセスの基本 == | |||
<syntaxhighlight lang="csharp"> | |||
// (1)コマンドが実行されたエディタのハンドルから、エディタオブジェクトを生成 | |||
var editor = new Editor(hWnd); | var editor = new Editor(hWnd); | ||
</ | </syntaxhighlight> | ||
ですが、この中で hWnd は OnCommand | |||
ですが、この中で hWnd は OnCommand の引数で、自動で渡されるハンドルです。これは「コマンドが実行された Editor のハンドル」です。 | |||
プラグインではマクロのように Editors -> Editor -> Documents -> Document | プラグインではマクロのように Editors -> Editor -> Documents -> Document のように独立したオブジェクトになっておらず、直接アクセスすることもできません。プラグインでの基本的なアクセス単位は全て「Editor」に対してとなります。その時の操作は常に「ActiveDocument」への操作となります。 | ||
では複数のタブがある場合に、別のタブを操作するにはどうするかと言えば、ActiveDocument の切り替え、つまり処理対象のタブをアクティブにします。Mery.DotNetLib.Editor.SetActiveDocument() というメソッドが用意されているので、これで別のタブをアクティブにし処理をします。元の状態に戻す必要があるならば、最初のアクティブなタブを覚えておき、最後に戻してあげる必要があります。 | |||
では複数のタブがある場合に、別のタブを操作するにはどうするかと言えば、ActiveDocument | このように、マクロとプラグインではアクセスの単位が異なります。マクロで用意されている機能のほとんどはプラグインでも利用可能ですが、その使用方法には違いがあることに注意してください(さらに、マクロで提供されていてプラグインで提供されていない機能もあるので、プラグインも万能ではありません)。 | ||
= タブ操作 = | == タブ操作 == | ||
全タブへの操作のサンプルを書いてみます。 | 全タブへの操作のサンプルを書いてみます。 | ||
< | <syntaxhighlight lang="csharp"> | ||
// | // (1)コマンドが実行されたエディタのハンドルから、エディタオブジェクトを生成 | ||
var editor = new Editor(hWnd); | var editor = new Editor(hWnd); | ||
// | // (2)現在アクティブなタブのインデックス番号を取得 | ||
int activedIndex = editor.GetActiveDocumentIndex(); | int activedIndex = editor.GetActiveDocumentIndex(); | ||
// | // (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> | ||
複数のタブを開いて実行してみてください。全てのタブの合計文字数が表示されるはずです。 | |||
(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 で確実に描画を再開できるようにする必要があります。 | |||
プラグインはマクロより危険な状態になりやすいため、扱いには気をつけましょう。 | プラグインはマクロより危険な状態になりやすいため、扱いには気をつけましょう。 |
2023年5月20日 (土) 02:14時点における最新版
概要編集
基本的なテキスト操作、およびプラグインからの操作の考え方です。
テキスト操作編集
プラグインでやる意味は非常に薄いのですが、マクロと同じことをやってみます。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 で確実に描画を再開できるようにする必要があります。
プラグインはマクロより危険な状態になりやすいため、扱いには気をつけましょう。