マーキング機能

概要

Azukiのマーキング機能は、テキストの一部分に対して文書種別(XMLファイル、C/C++ソースファイルなど)の構文や文法とは異なる観点から付加情報(メタ情報)を設定します。

マーキング機能の使い方には『スペルミスが検出された単語に「スペルミスをしている」という付加情報を設定する』あるいは『プログラム言語のコンパイルエラーが検出された部分に「コンパイルエラーが検出された」という付加情報を設定する』といった使い方があります。付加情報が設定された箇所にはアンダーラインを引くなどの装飾表示ができ、また該当箇所にマウスカーソルを移動すると通常とは異なるカーソルを表示させることができます。そして付加情報は後からAPIで取得できますので、クリックイベント等をきっかけに付加情報によって異なるアクションを実行できるようになります(たとえば右クリックイベント発生時にクリック箇所がスペルミス単語であった場合、右クリックメニューにスペリング修正候補を並べる、など)。なお、Azukiが内蔵しているURLハイライト機能はマーキング機能で実現されています。

以後、マーキング機能で付加情報を設定することを指して「マークする」と記します。

使い方

付加情報の定義

マーキング機能を使うにあたって最初に行うことは、マーキング機能で扱いたい付加情報をAzukiに登録することです。マーキング機能で扱うすべての付加情報はMarkingクラスで管理されており、Marking.Registerメソッドで登録・変更できます。なお内蔵URIマーカーが使うURI用の付加情報は最初から組み込まれており、変更できません。

付加情報はMarkingInfoクラスで表します。MarkingInfoクラスは付加情報を識別するためのID、付加情報に付ける名前、そして付加情報が設定された文字列の上にマウスカーソルが来たときに使うカーソル画像の種類を設定します。付加情報のIDはアプリケーション全体の中で各付加情報を一意に特定する数値です。名前は通常「この文字列は○○である」の○○の部分になると思います。

付加情報が設定された文字列の表示方法を設定する

付加情報の設定された文字列がどのように表示されるかは、ColorScheme.SetMarkingDecorationメソッドを使って設定します。たとえばLineType.WavedとColor.Redで初期化したUnderlineTextDecorationオブジェクトを使えば、該当する付加情報が設定された文字列の下に赤い波線が引かれます。あるいはLineType.DottedとColor.Transparentで初期化したUnderlineTextDecorationオブジェクトを使えば、対象文字列と同じ色で点線のアンダーラインが引かれます。

文字列に付加情報を設定・削除する

付加情報は同じテキスト部分に複数設定できます。テキスト部分をマークするにはDocument.Markメソッドを使います。またマークされた付加情報を削除するにはDocument.Unmarkメソッドを使います。

文字列の付加情報を取得する

各文字に設定された付加情報を取得するにはDocument.GetMarkingsAtメソッドを使います。また、ある文字が特定のマーキング情報でマークされているかどうかを確認する場合にはDocument.IsMarkedが使えます。

Document.GetMarkedRangeメソッドを使うと、指定した付加情報でマークされた文字が前後に何文字続いているかを検索し、その範囲を取得できます。マーキング機能の用途では同じ付加情報が付加された範囲を単位に操作することが多いので、このメソッドは文字単位で操作するメソッドより便利に使えると思います。また、指定した付加情報でマークされた文字列を取得したいだけの場合にはDocument.GetMarkedTextメソッドが便利です。

マークされた文字列のクリック等への反応

マークされた文字列をダブルクリックしたときの動作など、その文字列に対するユーザのアクションを扱う方法について説明します。

キーボード操作でのアクションは、キー押し下げイベント等のイベントハンドラ中でキャレットの位置を元にDocument.GetMarkingsAtを使って設定された付加情報を取得し、適宜処理を実行すれば実装可能です。

マウス操作の場合は、AzukiControlなどのUIモジュールが発生させるマウス関連イベントのEventArgsから情報を得て処理を実行してください。Azuki 1.6以降、マウス関連イベントのイベントハンドラに渡されるEventArgsは「IMouseEventArgsインタフェースを実装したオブジェクト」に変更され、Azuki独自の追加情報を含むようになりました。そのためマウス関連イベントのイベントハンドラ側でEventArgsのオブジェクトをIMouseEventArgsにキャストすれば、マウスイベントが発生した文字のインデックスなどの情報を取得できます。インデックスを取得したらDocument.GetMarkingsAtを使ってマーキング情報を取得し、適宜処理を実行してください。

余談

IMouseEventArgs導入の背景

IMouseEventArgsはイベントが発生した位置のインデックスを取得するだけであればわざわざ導入する必要もありませんでした。導入した本当の理由は、「マウスイベントをキャンセル可能にしたかった」からです。たとえばAnnで実装しているダブルクリックによるURLジャンプ機能では、Azukiの標準動作である「ダブルクリックで単語を選択する」動作が実行されてしまうと困ります(ブラウザでURLが開かれると同時にURL文字列の一部が選択される)。そこでHandledプロパティを持つIMouseEventArgsを実装したオブジェクトをイベント発生時に投げるようにしました。イベントハンドラが同プロパティをtrueにすれば、Azukiは標準動作を行いません。これが同インタフェース導入の背景となっています。

データ構造に関して

Azuki 1.6のマーキング機能では、1つ1つの文字を単位に、8種類の付加情報を同時に設定できます。また1種類だけURIのマーキング用に内部で占有していますので、プログラマは7種類の付加情報を自由に登録して使うことができます。

やや突っ込んだ話になりますが、Azukiは付加情報を文字ごとに1バイトの領域を用意し、その各ビットをマーキングIDに割り当てて記録しています(ビットマスク)。上限の8という数字はこの構造に由来するものです。また、このデータ構造はメモリ領域の使用効率が悪い問題もあります。将来的にはRLE圧縮を導入して効率化し、同時に文字ごとに4バイトの領域を用意するよう拡張する予定です。…Scintillaと同じように。

なお、内部データ構造であるビットマスクを直接使って操作するメソッドも存在しますが、それらは処理効率を上げるために内部で使っているメソッドです。よほどパフォーマンスを最適化したい場合を除いて、それらをプログラマが使う必要はありません。