CDTベースのエディターを構築する、第2回: CDTの中でテキストを表示

構文スタイリングを使ってソースコードを強調表示する

「CDT ベースのエディターを構築する」シリーズ第2回の今回は、Eclipse CDT (C/C++ Development Tooling) でのテキスト表示について紹介します。テキスト表示は、CDT にとって重要な利点です。明確に色分けされた表示によって、コードの読み取りやナビゲートが容易になります。この動作を理解することは、CDT コードを理解しようとする場合にも、あるいは完全機能のソース・エディターを独自に作ろうとする場合にも、非常に重要です。しかも、CDT のテキスト表示を実現するための機構は、もっと重要な機能、つまり自動構文解析のためにも必要なのです。

Matthew Scarpino, Java Developer, Eclipse Engineering, LLC

Matthew Scarpino は、Eclipse Engineering LLC のプロジェクト・マネージャーであり、Java 開発者です。彼は SWT/JFace in Action の中心著者であり、また SWT (Standard Widget Toolkit) に対して、小さいながらも重要な貢献をしました。彼が好きなものはアイルランドの民族音楽やマラソン、William Blake の詩、そして GEF (Graphical Editing Framework) です。


developerWorks 貢献著者レベル

2006年 9月 19日

CDT のテキスト表示の紹介

CDT エディターは、入力される文字それぞれに対して、驚くほど多くのタスクを実行します。文書のパーティションに変化がないかをチェックし、テキストをさらに分割するためのルールを有効にします。入力された文字が機能を完了すると、テキスト・スペースを最小限にとどめるためのサブルーチンを有効にします。もしその文字によって単語が完成する場合には、その単語をインデックスに追加すべきかどうかを判断します。さらに、その文字が C/C++ 文書の構造の中に受け入れられるものかどうかを判断します。もし受け入れられるものであれば、CDT エディター内部の DOM (Document Object Model) を更新します。受け入れられないものであれば、アノテーションによってエラーをレポートします。

CDT のイベント処理のすべての側面を説明することは、この記事の範囲外です。そこで、構文スタイリングに焦点を当てることにしましょう。ここでは、CDT エディターが、ソース・コードの構造に基づいて、どのようにテキストの色やフォント・スタイルを変えるのかについて説明します。この説明によって、キー入力に対する CDT エディターの応答を示すだけではなく、ここで説明するオブジェクトやプロセスを、このシリーズの第 3 回でも利用することができます (第 3 回では CDT の構文解析について説明します)。

私は第 1 回の BBCDT (Bare Bones C/C++ Development Tool) を更新して、同じテキストを表示するようにしました。新しいクラスは、org.bbcdt.dworks.internal.ui.text パッケージと org.bbcdt.dworks.core.parser パッケージの中にあります。BBCDT ソース・ファイルに有効なコードを入力すると、完全な CDT で皆さんが見慣れたものと同じ構文スタイリングが表示されるはずです (図 1)。このコードの入手方法は、「ダウンロード」セクションを見てください。

図 1. CDT の構文スタイリング
CDT の構文スタイリング

CDT の構文スタイリングのプロセス

構文スタイリングは一般的で装飾的なものに思えるかもしれませんが、そのプロセスは単純ではありません。これから説明するように、その裏では大量の作業が行われているのです。幸いなことに、いったんこれを理解できれば、色やフォント・スタイルを自由にカスタマイズできるようになり、CDT エディターの見え方を自分の思いどおりに加工することができます。こうしたクラスの大部分は Eclipse のテキスト・エディターの API (application program interface) の一部なので、皆さん独自のエディターの中で直接使うことができます。

簡単に言うと、構文スタイリングの最終的な目標は、ユーザーが適切なシーケンスで有効な C/C++ コードを入力する毎に、テキストの一部に対して TextPresentation オブジェクトを作成することです。このプロセスには、次の 4 つのステップがあります。

  1. キー入力に対して Document が DocumentEvent を作成します。
  2. FastPartitioner が Document のパーティションを更新します。
  3. ビューアーが PresentationReconciler に警告します。PresentationReconciler は、変更されたパーティションを DefaultDamagerRepairer を使って分析します。
  4. DefaultDamagerRepairer は、テキストの色とスタイルを更新する TextPresentation を、ルールを使って作成します。

ステップ 1. SourceViewer と Document

CEditor が行う作成動作の中で、CEditor が最初に作成するオブジェクトの 1 つが CSourceViewer です。このオブジェクトは、CEditor の StyledText ウィジェットを構築するだけではなく、受け取ったすべてのイベントの処理も行います。特に、キー入力には VerifyListener を使って応答します。必要に応じて VerifyEvents を他のオブジェクトに転送することができますが、CSourceViewer はデフォルトでエディターの Document に通知します。

第 1 回で触れたように、Document はエディターの情報を保持し、DocumentProvider はその情報をエディターの入力ファイルのテキストで初期化します。同様に SourceViewer は、エディターのテキストで Document を更新します。それを行うためには DocumentCommand を使います。各コマンドは、追加されたテキストと、Document 内でのテキストの位置を保持します。コマンドが実行されると、コマンドは Document の中のモデル情報を更新します。

CDT エディターが、華やかな機能を数多く備えていても単純な StyledText ウィジェットであるのと同じように、Document は基本的に String (技術的には ITextStore ) です。Document はテキストの他に、String のサブセクションを表す一連の Position を含んでいます。各 Position は長さとオフセットを持っており、TypedPosition は関連付けられた名前を持っています。TypedPosition はパーティションの表現に使われるため、ここでは特に重要です。

DocumentCommand は実行に際して、まずキャレット (縦長のバーによるカーソル・バー) に対応する Position を更新することから始めます。次に DocumentCommand は Document.replace() を呼び、それによって Document の ITextStore の中の文字が変更されます。これが終了すると Document は最新になり、Document は登録されたすべての DocumentListener に対して DocumentEvent を送信します。

ステップ 2. FastPartitioner による文書のパーティショニング

CDT では、テキストが入力される毎に文書全体を分析するのではなく、Eclipse のテキスト・エディター API が提供する、分割統治 (divide-and-conquer) 手法を使います。つまり CDT は、パーティションと呼ばれる相互排他的な区画に Document を分割するのです。こうすることによって、変更されたテキストを含むパーティションのみが検証されます。例えば、複数行にまたがるコメントの中の単語を変更した場合には、CDT はそのコメントを含んだパーティションを分析し、残りのコードは分析しません。

ユーザー・インターフェース (UI) プラグインの中の plugin.xml ファイルは、org.eclipse.core.filebuffers.documentSetup エクステンション・ポイントのエクステンションを作成します。このエクステンションは、CDT Document が作成されると、Document の分割を判断して管理するために、Document を FastPartitioner オブジェクトに接続します。

CDT では、このパーティショナーは、4 つの String を持つ配列によって初期化されます。この 4 つの String は、下記のようなパーティションそれぞれに対して名前をつけます。

モジュラー性の裏側

Eclipse の GEF (Graphical Editor Framework) API を使ってグラフィカル・エディターを作成する際には、明らかにコンサーンの分離が必要です。MVC (Model-View-Controller) のモザイクの中に、何百という単一機能のクラスがあるのは嬉しい限りです。これは確かに複雑ですが、GEF エディターの中の図形や接続、相互関係などを考えれば、この複雑さは十分理解できます。

しかし私の意見では、テキスト・エディターにはこうしたレベルの複雑さは必要ないと思います。結局のところ、しょせんはテキストなのです。文字を一時的に青くしたり太字にしたりする度に、何ページものドキュメンテーションを読む必要はないはずです。皆さんは、パーティショニングやルール処理に関する私の説明が細かすぎると思うかもしれませんが、実際には私は非常に多くのことを省略しているのです。怠惰で無関心な私には任せられないと思う人は、私が手をつけなかったものを何とかしてくれませんか。

  • 複数行のコメント
  • 単一行のコメント
  • ストリング
  • 文字

また CDT は、FastCPartitionScanner を使ってパーティショナーも初期化します。簡単に言うと Eclipse のスキャナーは、Document 範囲の中にある文字を、任意のデータ Object を持つ一連の Token に変換するのです。FastCPartitionScanner の場合、各 Token は、現在のパーティションに名前をつける 4 つの String の 1 つを含んでいます。

Document は、他のリスナーに警告する前に、パーティショナー群に対して DocumentEvents を送ります。この場合には、FastPartitioner に対してのみ送ります。FastPartitioner はこのイベントを使って、変更されたテキストを含む最初の行と、その行の開始点を含んだ TypedPosition を見つけます。そして FastCPartitionScanner に対して、その範囲のテキストを Token に変換するように伝えます。

FastCPartitionScanner は、BufferedDocumentScanner を使って Document の文字を読み取ります。次に、ステート・マシン (つまり switch 文) を使って、読み取られる文字がパーティションの最後を表しているかどうかを判断します。もし最後であれば、FastCPartitionScanner は、そのパーティションに対する Token を返し、そのオフセットと長さをレポートします。FastPartitioner は、それを使って Document のパーティション・リストを更新し、これでパーティショニング・プロセスが終了します。

ステップ 3. 変更されたテキストをDefaultDamagerRepairer で分析する

CSourceViewer クラスは重要であるにも関わらず、その中にはほとんどコードがありません。CSourceViewerConfiguration は、ビューアー用の機能を実行するオブジェクトを多数提供しています。これらの中で最も重要なものの 1 つが、各パーティションに対して DefaultDamagerRepairer を作成する PresentationReconciler です。リスト 1 のコードは、これを実現しています。

リスト 1. PresentationReconciler に DefaultDamagerRepairers を追加する
DefaultDamagerRepairer dr= new \
DefaultDamagerRepairer(getSinglelineCommentScanner());		
reconciler.setDamager(dr, ICPartitions.C_SINGLE_LINE_COMMENT);
reconciler.setRepairer(dr, ICPartitions.C_SINGLE_LINE_COMMENT);
		
dr= new DefaultDamagerRepairer(getMultilineCommentScanner());		
reconciler.setDamager(dr, ICPartitions.C_MULTILINE_COMMENT);
reconciler.setRepairer(dr, ICPartitions.C_MULTILINE_COMMENT);

dr= new DefaultDamagerRepairer(getStringScanner());
reconciler.setDamager(dr, ICPartitions.C_STRING);
reconciler.setRepairer(dr, ICPartitions.C_STRING);
		
dr= new DefaultDamagerRepairer(getStringScanner());
reconciler.setDamager(dr, ICPartitions.C_CHARACTER);
reconciler.setRepairer(dr, ICPartitions.C_CHARACTER);

String language = ((CSourceViewer)sourceViewer).getDisplayLanguage();
if(language.equals(CEditor.LANGUAGE_CPP)) {
	scanner= getCppCodeScanner();
} else {
	scanner= getCCodeScanner();
}

dr= new DefaultDamagerRepairer(scanner);
reconciler.setDamager(dr, IDocument.DEFAULT_CONTENT_TYPE);
reconciler.setRepairer(dr, IDocument.DEFAULT_CONTENT_TYPE);

この先を続ける前に、damager と repairer が何をするのかを説明する必要があるでしょう。基本的に IPresentationDamager の目的は、特定の DocumentEvent による影響が文書のパーティションのどの範囲に及ぶのかを判断することです。従って、名前は恐ろしげですが、damager は実際には単なるダメージ・アナライザーなのです。一方、IPresentationRepairer は damager の結果を使って、テキストの色やスタイルを変更するために必要な情報を含んだ TextPresentation を作成します。簡単に言えば、DefaultDamagerRepairer は、この両方の機能を実行し、パーティションに対して TextPresentation を作成することでイベントに応答するのです。

CSourceViewer は動作を開始する前に、CSourceViewerConfiguration が提供するオブジェクトにアクセスし、PresentationReconciler をインストールします。このインストールによって、リコンサイラーが TextEvent をリッスンできるようになります。TextEvent は DocumentEvent と似ていますが、TextEvents には、新しいテキストと置き換えられたテキストの両方が含まれている点が異なります。DocumentEvent は新しいテキストしか含んでいません。

PresentationReconciler は TextEvent を受け取ると、変更されたテキストを含むパーティションがどれかを判断し、適切な DefaultDamagerRepairer に警告します。リコンサイラーはパーティション毎に別々の DefaultDamagerRepairer を持っていますが、DefaultDamagerRepairer はどの場合にも同じことをします。DefaultDamagerRepairer は FastPartitioner と同様、ダメージを含んだ最初の行の開始点と、そのパーティションの開始点を判断します。これらのうちの最大のものが、ダメージの開始点と判断されます。またダメージの最後は、パーティション内の最後の位置と、パーティション内で変更されていないテキストの最後の位置の最小値を計算して見つけます。そして DefaultDamagerRepairer は、オフセットと長さを提供することで Document の 1 セクションを表現する、IRegion を返します。

ステップ 4. ルール処理

PresentationReconciler はダメージ情報を受け取ると、そのパーティションの DefaultDamagerRepairer に新しい TextPresentation を作成するように伝え、それをダメージを受けた部分に適用します。リスト 1 に示したように、各 DefaultDamagerRepairer は、そのパーティションに適したスキャナーによって初期化されます。DefaultDamagerRepairer は起動すると、まずダメージを受けた部分を分析するようにスキャナーに伝え、そして Token を生成します。

CDT では、コメント・パーティションは CCommentScanner とその文字によってスキャンされ、String パーティションは SingleTokenCScanner によってスキャンされます。他のパーティションでカバーされないテキストを含むデフォルト・パーティションは、コード言語に応じて、CppCodeScanner または CCodeScanner にスキャンされます。これらの各スキャナーは RuleBasedScanner のサブクラスであり、それぞれ IRule の List を使って、テキストの中に検出されるパターンに対応した Token を作成します。

こうした IRule は特に重要であり、Eclipse のテキスト・エディター API には、次の 5 つの実装クラスが用意されています。

WordRule
特定の単語が見つかった場合に Token を返します
SingleLineRule
ある表現が 1 行のテキストの中に見つかった場合に Token を返します
MultiLineRule
ある表現が複数行のテキストの中に見つかった場合に Token を返します
NumberRule
テキストの中のすべての数字に対して Token を返します
WhitespaceRule
空白が検出された場合に Token を返します

こうしたルールをスキャナーがどう使うのかを理解するために、リスト 2 を見てください。このリストは、CCodeScanner が String と数字を検出するためのルールを作成するコード部分を示しています。テキストの一部分がルール・パターンと一致すると、スキャナーは適切な Token を返します。

リスト 2. RuleBasedScanner にルールを追加する
Token token= getToken(ICColorConstants.C_STRING);
rules.add(new SingleLineRule("'", "'", token, '\\'));

token = getToken(ICColorConstants.C_NUMBER);
NumberRule numberRule = new NumberRule(token);
rules.add(numberRule);

これらの Token は、パーティショニング・スキャナーが返すものと同様な String を含んでいます。しかし、こうした Sting は、パーティション名ではなく、TextAttribute を表します。TextAttribute は、対象となるテキスト部分の表示方法、つまりテキストの背景色や前景色、そしてスタイル (SWT.BOLD、SWT.ITALIC、または SWT.NORMAL) を表す整数を記述します。例えば CCodeScanner は、テキストの中にある C/C++ キーワードを、WordRule を使って検出します。WordRule がキーワードを検出すると、そのキーワードの Token の内容はエディターに対して、そのキーワードの表示方法を伝えます。

DefaultDamagerRepairer はルールから Token を受け取ると、その Token の TextAttribute を取得し、そのテキストをどう表示すべきかを判断します。そして次に、Document テキストの対象部分のスタイルをコントロールする、StyleRange オブジェクトを作成します。DefaultDamagerRepairer は、TextPresentation に StyleRange を追加すると、その役割を終えます。PresentationReconciler は TextPresentation をビューアーに送り、ビューアーは新しい色とフォント・スタイルで StyledText ウィジェットを更新します。


BBCDT を更新する

構文スタイリング・コードに関して、CDT と BBCDT との間での唯一の主な違いは、プリファレンスの使い方です。CDT では、Eclipse ワークベンチのプリファレンスを更新することで、C/C++ テキストの色やフォント・スタイルをコントロールできます。しかし BBCDT はプリファレンスを使いません。従って、各 Token String に関連付けられたスタイルを変更するためには、org.dworks.bbcdt.internal.ui.text パッケージの中にある CColorManager クラスのコンストラクターのコードを変更する必要があります。

図 2 は、デフォルト設定の場合に BBCDT のテキストがどう表示されるかを示しています。

図 2. BBCDT の構文スタイリング
BBCDT の構文スタイリング

Eclipse のインストールにプラグインを追加した後、File > New > Project をクリックし、C または C++ オプションを選択すると、BBCDT プロジェクトを作成することができます。ファイルを作成するには、New > Other をクリックし、C または C++ オプションを選択します。


まとめ

この記事は、キー入力によって新しい Document パーティションが作成され、あるいはエディターのテキスト・スタイルが変更されることを説明しました。イベント処理は単純ではありませんが、コンサーンが分離されているため、そのプロセスを妨害せずに任意の側面をカスタマイズすることができます。また、これで Eclipse のテキスト・エディター・イベントの動作が説明できたので、今後の記事では、この機能に依存する高度な機能、CDT の自動構文解析を取り上げることにします。


ダウンロード

内容ファイル名サイズ
Part 2 source codeos-ecl-cdt2.zip552KB

参考文献

学ぶために

  • Eclipse.org で Eclipse CDT について調べてみてください。
  • CDT 開発チームのリーダー、Doug Schaefer による素晴らしいブログを読んでください。
  • Eclipse Foundation について、そしてその数多くのプロジェクトについて学んでください。
  • Eclipse プラットフォームへの素晴らしい入門記事として、「Eclipse Platform入門」を読んでください。
  • IBM developerWorks の Eclipse project resources を利用して、皆さんの Eclipse スキルを磨いてください。
  • developerWork には他にも Eclipse に関連する資料が豊富に用意されています。
  • developerWorks の Open source ゾーンを訪れてください。オープンソース技術を使った開発や、IBM 製品でオープンソース技術を使用するためのハウ・ツー情報やツール、プロジェクトの更新情報など、豊富な情報が用意されています。
  • developerWorks technical events and webcasts で最新情報を入手してください。

製品や技術を入手するために

  • Eclipse.org から Eclipse CDT をダウンロードしてください。
  • IBM alphaWorks に用意された、最新の Eclipse technology downloads を調べてみてください。
  • 皆さんの次期オープンソース開発プロジェクトを、IBM trial software を使って構築してください。ダウンロード、あるいは DVD で入手することができます。

議論するために

  • Eclipse newsgroups は、Eclipse を利用し、拡張することに関心を持つ人達のために、豊富なリソースを提供しています。
  • developerWorks blogs から developerWorks のコミュニティーに加わってください。

コメント

developerWorks: サイン・イン

必須フィールドは(*)で示されます。


IBM ID が必要ですか?
IBM IDをお忘れですか?


パスワードをお忘れですか?
パスワードの変更

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


お客様が developerWorks に初めてサインインすると、お客様のプロフィールが作成されます。プロフィールで選択した情報(名前、国/地域や会社名)は公開され、投稿するコンテンツと一緒に表示されますが、いつでもこれらの情報を更新できます。

送信されたすべての情報は安全です。

ディスプレイ・ネームを選択してください



developerWorks に初めてサインインするとプロフィールが作成されますので、その際にディスプレイ・ネームを選択する必要があります。ディスプレイ・ネームは、お客様が developerWorks に投稿するコンテンツと一緒に表示されます。

ディスプレイ・ネームは、3文字から31文字の範囲で指定し、かつ developerWorks コミュニティーでユニークである必要があります。また、プライバシー上の理由でお客様の電子メール・アドレスは使用しないでください。

必須フィールドは(*)で示されます。

3文字から31文字の範囲で指定し

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


送信されたすべての情報は安全です。


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=60
Zone=Open source
ArticleID=236841
ArticleTitle=CDTベースのエディターを構築する、第2回: CDTの中でテキストを表示
publish-date=09192006