プラグイン仕様(案) ver 2010/08/09 1. はじめに プラグインは拡張機能、アドオンとも呼ばれ、エディタ本体に後から追加し さまざまな機能を提供するものです。 てっとりばやくプラグインを実装するには、「4. 現時点の実装仕様」を見ながら、 サンプルプラグインを参考にしてください。 2. 主なプラグイン対象箇所 ・タイプ別のアウトライン解析 ・タイプ別の自動インデント ・コマンド追加 [ツール]メニューの下にメニュー項目を追加できる。 メニューから呼び出すと、決まった機能を実行する。 ツールバーにアイコンを登録できる。 ・etc... 3. 方向性 3.1. 利用イメージ ・ネット上からプラグインのzipファイルをダウンロードする。 ・ダウンロードしたファイルを解凍し、sakura/plugins/ 配下にフォルダを作って配置する。 あるいはユーザ別設定が有効な場合、ユーザ別フォルダ(sakura.iniがある)配下の pluginsフォルダに配置することができる。 以下、pluginsをプラグインフォルダと呼び、その下のプラグインごとのフォルダを 個別フォルダと呼ぶ。 ・設定画面からプラグインの追加を行う。 ・エディタ本体を再起動するとプラグインが読み込まれ、利用可能な状態になる。 ・プラグインのアンインストールは、設定画面からプラグインの削除を行う。 ・プラグインの新バージョンが出た場合は、個別フォルダの内容を置き換えて エディタを再起動することで更新できる。 3.2. 動き ・<--削除-->コントロールプロセスを起動すると、プラグインフォルダ配下を検索する。 →コントロールプロセスの起動に時間がかかるとエディタがビジーエラーを出すため、 コントロールプロセスでのプラグイン初期化は行わない。 ・エディタプロセスの起動時に、共通情報のプラグインテーブルを参照し、インストール済みの プラグインに対して以下の読み込み操作を行う。 個別フォルダ内にはプラグインの情報を記述したテキストファイル(plugin.def)があり、 エディタはまずこのテキストファイルを読み込む。 以下、このファイルをプラグイン定義ファイルと呼ぶ。 また、プラグインフォルダ直下に、プラグインごとのテキストファイルを置き、 そこにプラグインのオプションなど可変の情報を記述する。 以下、このファイルをオプションファイルと呼ぶ。 ファイル名は "個別フォルダ名.ini" とする。 定義ファイルのOptionセクションで指定した項目は、共通設定のプラグイン個別設定から 値を編集できる。 ・プラグイン定義ファイルの内容に従い、メモリ上にプラグインオブジェクトを作成する。 ・プラグイン定義ファイルの内容に従い、エディタ内の各機能にあらかじめ設定された、 プラグイン可能な箇所に、プラグインの関数(プラグ)を関連付ける。 以下、プラグイン可能な箇所をジャック(プラグをさす穴)と呼ぶ。 ・エディタを操作し、ジャックに達したら、関連付けられている全ての プラグを実行する。アウトライン解析やスマートインデントはタイプ別設定で 選択したもの一つだけを実行する。 ・<--削除-->エディタを全終了すると、ロードされている全てのプラグインを解放する。 →エディタプロセス終了で解放する。 ・プラグインは、コンパイルされたDLLや、WSHで実行できるマクロ言語などに 対応する。ただし一度読み込んだ後は、違いを意識せずに操作できる。 3.3. フォルダ構成 Program Files/ └ sakura/ ├ sakura.ini └ plugins/ ├ ○○.ini // オプションファイル ├ ××.ini ├ ○○/ // 個別フォルダ │ ├ plugin.def // プラグイン定義ファイル │ ├ readme.txt // プログラム等 │ ├ ○○_A.js │ └ ○○_B.js └ ××/ ├ plugin.def └ ××.dll // dllにも対応できたり・・・ 3.4. ER図 ・「エディタ本体」1 - *「プラグイン」 複数のプラグインをエディタで使うことができる。 ・「エディタ本体」1 - *「ジャック」 エディタには複数のジャック(プラグイン可能箇所)がある。 ・「プラグイン」 1 - *「プラグ」 プラグインは複数のプラグ(実行単位)を持つ。 ・「ジャック」 1 - *「プラグ」 ジャックには複数のプラグを関連付けることができる。 3.5. 課題 ・ファイルタイプの判定 例えばJSPのアウトライン解析プラグインをインストール済みのとき、HTMLを 編集中はその機能を使いたくないかもしれない。現在のファイルタイプや 拡張子から、プラグインの有効/無効を判断する必要がある。 →アウトライン解析などタイプ別設定で選択可能なものは選択肢を追加して対応する。  タイプ別設定にないものが現われた場合、プラグインのロジックでどうにかするか、  はたまたタイプ別設定画面を拡張して選択肢を新規追加できるのか。 ・機能の上書き 現在C++の自動インデントが実装されているが、別の自動インデントプラグインを インストールすると現在の機能は上書きされるのか。それとも順番に両方とも 実行するのか。 ジャックごとに複数のプラグを登録可なのかを決める必要がある。 →選択肢から選択させることで解決。 ・機能の重複 同じジャックに関連付けられた同種の機能があった場合、優先度の ようなものを設定するのか。 案)エディタ本体のプラグイン設定でプラグインの優先順位を指定できるように する。設定した優先順位はオプションファイルに記録。 競合不可の場合は、優先度の低いプラグインは使用されない。 →たとえばアウトライン解析プラグインを複数インストールした場合、  複数の選択肢の中から選択できるので、この課題は解決している。 ・DLLでもWSHと同じようにEditorオブジェクトの機能を使えるのか? ・コマンドを動的に追加できるのか?reallocする?追加したコマンドをツールバーに 追加した場合、設定ファイルにどのように記録されるか。 →ツールバーボタンのテーブルはvector化した。設定ファイルへはとりあえず改造の  手間が少ないよう、2xxyyのような番号で記録している。xxはプラグインを識別する  番号であり(少なくともローカルPC内で)、yyはプラグイン中でプラグを識別する  番号である。この方針だと、同時に使用できるプラグインが最大99個に制限される。  また、xxをどうやって割り振るのか未定。  →プラグイン番号xxの割り振りは、プラグインのインストール時にプラグイン   テーブルの空き番号を割り当てることにした。 ・プラグインの読み込みをNormalProcessの起動ごとに行うのは無駄なので、 ControlProcess起動時に1度だけ読み込んで使いまわしたいが、プロセスまたがり なので共有メモリを使わなければならない。今の作りはSTLとか使いまくりなので 共有メモリにしづらい。 ・ツールバーの設定はアイコンBMPの番号で記録されているため、プラグインコマンドを 登録した後プラグインの削除などを行うと、次回起動時にアイコンがずれてしまう。 →ボタン番号で記憶している限り解決しないため、プラグインコマンドのみINI上では 「プラグイン名/プラグ番号」の形式で記録するようにした。 3.6. 欲しい機能 ・プラグイン読み込み機能 個別フォルダにあるプラグイン定義ファイルを読み込み、妥当な内容で あるかチェックする。内容にエラーがある場合はエラーダイアログを出し、 そのプラグインを読み込まない。妥当であれば、初期化処理を行う。 ・プラグ登録機能 プラグイン可能な場所(=ジャック)に対し、プラグインを関連付ける。 その際、ファイルタイプや拡張子によって登録可否を判断する。 また、同種の機能が競合したとき、優先度の低いプラグインは関連付けしない。 ・ジャック実行機能 プラグイン可能な箇所に処理が来たとき、各プラグインの関数を呼び出す機能。 ジャックごとに独自のデータオブジェクトを関数に渡すことで 動作を変更することができる。 ・コマンド登録機能 プラグインで独自のコマンドをエディタに追加する。追加したコマンドはメニューに 登録したり、ツールバーに登録して使える。アイコンも設定できればいい。 4. 現時点の実装仕様 4.1. プラグイン定義ファイル(plugin.def) プラグイン定義ファイルは以下の内容を含む。 ■全プラグイン共通 [Plugin] ・Id - プラグイン識別子。最大UNICODE63文字の、世界でユニークな文字列。 使用できる文字は、半角英数と「{}._-」のみとする。「,」と「/」は厳禁。 例){A8E7CC0A-032D-11DF-9E9A-333D56D89593}, jp.co.google.foo.BarPlugin, etc.. ・Name - プラグイン和名 ・Description - 簡潔な説明 ・Type - wsh / dll / ... ・Author - 作者名。Copyright等。 ・Version - バージョン。4個以内の数字を.でつなげたもの。 ・Url - 配布元URL [Plug] - ジャックと呼び出すハンドラの対応表 ジャック名=ハンドラ 一部のジャックには、Label属性を指定することができる。(Outline, SIndent) ジャック名.Label=ラベル文字列 例) Outline=xxxxx ←アウトライン解析時にxxxxxを実行する Outline.Label=yyy ←タイプ別設定の選択肢にyyyが追加される [Command] - 本体に追加するプラグインコマンドの定義 ・C[n]=ハンドラ C[n].Label = コマンド名 C[n].Icon = アイコンファイル(16x16のBMPファイル) (n=1〜99。欠番があるとその後は読み込まれない) [Option] - プラグインオプションの定義 ・O[n].Section = オプションファイル中のSection名(最初のみ必須) 未指定の場合には、直前の値を使用する。 O[n].Key = オプションファイル中のKey名(必須) O[n].Label = 表示項目 未指定の場合には、Keyを使用する。 O[n].Type = 型、以下に定義する、未定義、未指定の場合には文字列型として処理する。 Str : 文字列型(1024文字まで) Bool : ブール型 True で 1 False で 0 をプラグインオプションに設定する。 Int : 整数型(-2147483647〜2147483647、32bit符号付整数) (n=1〜99。欠番があっても読み込む) ■WSHプラグイン固有 ・Plug, Command セクションでのハンドラにはスクリプトファイル名を指定する。 例)パスは個別フォルダ起点の相対パスとする。 Outline=outline.js [Wsh] ・UseCache - 1:ディスクから読み込んだスクリプトをメモリ上にキャッシュする、0:キャッシュしない ■DLLプラグイン固有 ・Plug, Command セクションでのハンドラにはDLLがエクスポートする関数名を指定する。 [DLL] ・Name - DLLファイル名 4.2. インタフェースオブジェクト   JScriptやVBScriptから参照してエディタの情報取得・操作を行うオブジェクト。 4.2.1. Editor ・・・ おなじみEditorオブジェクト。 利用可能: マクロ、プラグイン(プラグ、コマンド) 4.2.2. Plugin ・・・ プラグインの共通機能を提供する。 利用可能: プラグイン(プラグ、コマンド) GetPluginDir() 個別フォルダのパスを返す。 GetDef(section, key) 定義ファイルの値を取得する。 GetOption(section, key) オプションファイルの値を取得する。 SetOption(section, key, value) オプションファイルの値を設定する。 AddCommand(handler, label, icon) コマンドを追加する。 GetCommandNo() 実行中のプラグ番号を取得する。 4.2.3. Outline ・・・ アウトライン解析用の機能を提供する。 利用可能: プラグイン(Outlineのみ) AddFuncInfo(論理行, 論理桁, 文字列, 付加情報) アウトライン解析に1行追加(対象:リスト、クラスツリー) AddFuncInfo2(論理行, 論理桁, 文字列, 深さ) アウトライン解析に1行追加(対象:ツリー) SetTitle(文字列) アウトライン解析ダイアログのタイトルを変える SetListType(整数) リストの種別を設定。 100:ツリー表示 200:オブジェクト指向言語のクラス・メソッドに適したツリー表示 300:リスト表示 ※以下の番号も使用できますが、推奨されません。100〜300を使用してください。 0:関数名と行番号のシンプルな一覧表 1:オブジェクト指向言語のクラスに適したツリー表示 3:汎用のツリー表示 8:VisualBasic向けの関数一覧表 4.2.4. Indent ・・・ スマートインデント用の機能を提供する。 利用可能: プラグイン(SIndentのみ) GetChar() 入力されたキーを取得する。Enterの場合 \r を返す。 4.3. 欠番 4.4. マクロ関連クラスの改造 WSHマクロで使用できるインタフェースオブジェクトを、Editorだけではなく複数扱えるように 修正した。 CWSHManager WSHマクロ実行の入り口。 .ExecKeyMacro関数の中でCWSHClientを作成する。 CWSHClient WSH呼び出しの入り口。 CIfObj 純粋なインタフェースオブジェクト - CWSHIfObj WSHマクロに特化したインタフェースオブジェクト - CEditorIfObj 今までのEditorオブジェクト - plugin/CPluginIfObj プラグイン共通機能を提供するPluginオブジェクト - plugin/COutlineIfObj アウトライン解析機能を提供するOutlineオブジェクト - CSmartIndentIfObj スマートインデント機能を提供するIndentオブジェクト(CViewCommander.cpp内) 4.5. プラグイン関連クラス構成 CPlug - CWSHPlug - CDllPlug CPlugin - CWSHPlugin - CDllPlugin CPluginManager CJackManager 4.6. プラグインコーディングガイド 4.6.1. アウトライン解析(ジャック名:Outline) アウトライン解析プラグインは、アウトライン解析ダイアログを表示する際に 一度だけ呼びだされる。プラグインコードは、編集中文書の先頭から1行ずつ 読み取り、条件に合致すればOutlineオブジェクトの AddFuncInfo, AddFuncInfo2 関数でアウトライン解析ダイアログに登録する。 アウトライン解析ダイアログはあらかじめ提供されている中から適当なものを Outline.SetListType関数で選択する。ダイアログのタイトルはOutline.SetTitle 関数で指定できる。 <疑似コード> Outline.SetListType( タイプ ) # タイプ: 4.2.3章を参照。 Outline.SetTitle( 'タイトル文字列' ) 行数 ← Editor.GetLineCount( 0 ) ループ: 1 ≦ カウンタ ≦ 行数 行 ← Editor.GetLineStr( カウンタ ) # 行末にEOLが1byte付加されていることに注意。 もし: 行 が 条件 に当てはまるなら Outline.AddFuncInfo( カウンタ, # 論理行番号 1, # 論理桁番号 '文字列', # ダイアログに表示する文字列 付加情報) # 追加行の種別を表す整数値 # もし:終わり カウンタ ← カウンタ + 1 ループ:終わり ※タイプで 3:汎用のツリー表示 を選択した場合は、AddFuncInfoの代わりに AddFuncInfo2関数を使用する。その場合、第4引数には階層を表す整数値を 渡す。 ※付加情報はタイプによって以下のように解釈される。 ・タイプ 200:クラスツリー の場合 1: 宣言 2: 通常の関数 3: クラス 4: 構造体 5: 列挙体 6: 共用体 7: 名前空間 8: インタフェース ・タイプ 8:VB関数一覧 の場合 1: 宣言 10: 関数宣言 20: プロシージャ宣言 11: 関数 21: プロシージャ 31: ■パッケージ仕様部 41: ■パッケージ本体部 50: PROC 51: ラベル 52: ENDP またリスト表示で付加情報に0x10000を加えると、その行はクリップボードコピーの対象外となる。 4.6.2. スマートインデント(ジャック名:SIndent) スマートインデントプラグインは、キーボードの文字キーを押下してから 実際に文字が表示されるまでの間に毎回呼びだされる。プラグインコードは、 編集中文書の先頭から1行ずつ読み取んで、現在行で適切なインデント幅を判断して スペースまたはタブを出力する。キー入力ごとにプラグインを呼び出すため、 WSHプラグインなら負荷軽減のためUseCacheを1にしたほうがよい。 Enterキーが入力された場合、カーソルが次の行に移動した状態でプラグイン が呼び出される。またタイプ別設定の自動インデントが有効の場合、自動インデント 後の状態でプラグインが呼び出される。改行されたかどうかは論理桁が1であるかで 判定するため、自動インデントはオフにしてもらう必要がある。 <疑似コード> 現在行 ← Editor.ExpandParameter("$y") 現在桁 ← Editor.ExpandParameter("$x") もし: 現在桁 が 1 でないなら 処理終了 # 改行のみをインデントタイミングとする場合 もし:終わり 行数 ← Editor.GetLineCount( 0 ) ループ: 1 ≦ カウンタ ≦ 行数 もし: カウンタ が 現在行 なら Editor.InsText( '適切なインデント文字列' ) 処理終了 もし: 終わり カウンタ ← カウンタ + 1 ループ:終わり 5. 参考 ・プラグイン対応アプリケーションについて http://hp.vector.co.jp/authors/VA041250/doc/plugin/ ・COM研究室 http://www5.plala.or.jp/atata/com/ ・Firefox 全般的に参考とした https://developer.mozilla.org/ja/Bundles ・Eclipse 全般的に参考とした2 http://www.genpaku.org/other/eclipse/plugin_architecturej/plugin_architecture.html ・Meryのコマンド追加機能 ・Poderosaのプラグイン機構