IntelliJ IDEAプラグイン開発。 パート5

このパートでは、強調表示、リンクシステム、オートコンプリート、コードナビゲーション。 前の部分はこちらです。


構文とエラーの強調表示



IDEAが対応するテキスト範囲を強調表示する方法を決定するために使用するクラスは、 TextAttributesKeyと呼ばれます 。 このクラスのインスタンスは、強調表示する必要のあるさまざまなタイプの要素(キーワード、数字、行、コメントなど)ごとに作成され、対応するタイプの要素に適用されるデフォルト属性を定義します(たとえば、キーワードは太字、青の数字、斜体の線、および緑の背景)。 エディターで使用される特定の属性へのTextAttributesKeyのマッピングはEditorColorsSchemeクラスによって定義され、プラグインが適切な構成インターフェースを提供する場合、ユーザーが構成できます。 強調表示では、複数のTextAttributeKeysの重複を使用できます。たとえば、1つのキーでスタイルを決定し、もう1つのキーで要素の色を決定できます。



基本的な構文の強調表示


構文とエラーの強調表示は、さまざまなレベルで実行されます。 最初は、字句解析の結果に基づいた構文の強調表示は、 SyntaxHighlighterインターフェイスを介して実行されます。 このインターフェイスは、トークンの各タイプのTextAttributeKeyインスタンスを返すため、特別な強調表示が必要です。 レクサーエラーを強調表示するために、無効な文字( HighligherColors.BAD_CHARACTER



)にはTextAttributeKeyクラスの標準オブジェクトが使用されます。



Intelli IDEA 12.1では、カラー管理スキームがわずかに変更され、回路設計者の作業が容易になり、スキームが元々意図されていなかった場合でも、異なるプログラミング言語で同じディスプレイが作成されます。 以前は、言語プラグインは、たとえば暗いテーマと常に互換性があるとは限らない固定の配色を使用していました。 新しい実装により、特定の言語ではなく、スキームに関連付けられた一連の標準テキスト属性への依存関係を決定できます。 特定の言語の属性は、スキーマデザイナによって引き続き設定できますが、現在はオプションです。 互換性の問題を回避するため、新しい配色には拡張子.iclsが付けられています。

現在、 DefaultLanguageHighlighterColorsクラスを使用して、テキスト属性を定義し、標準キーに依存しています。



例:PropertiesプラグインのSyntaxHighlighlighter実装



ハイライトの第2レベル-解析中に発生したエラーのハイライト。 特定のトークンチェーンが言語の文法と一致しない場合、PsiBuilder.error()メソッドを使用して無効なトークンを強調表示し、エラーメッセージを表示できます。



注釈


バックライトの第3レベルは、 Annotatorインターフェイスを使用して行われます。 プラグインは、 com.intellij.annotator



拡張com.intellij.annotator



で1つまたは複数の注釈を登録できます。その後、ユーザーの言語PSIツリーの要素を強調表示しながら、バックグラウンドプロセスで呼び出されます。 注釈は、構文だけでなくセマンティクスも分析できるため、エラーを処理および強調表示するためのより微妙なロジックを提供します。 注釈には、検出された問題を解決する機能(いわゆるクイックフィックス)が含まれる場合があります。



ファイルが変更されると、PSIツリーの変更された要素のみを処理するために、注釈がインクリメンタルに呼び出されます。

特定の範囲のテキストをエラーまたは警告として強調表示するために、注釈はAnnotationHolder型のオブジェクトでcreateErrorAnnotation()またはcreateWarningAnnotation()を呼び出し、オプションでAnnotatorクラスの返されたオブジェクトでregisterFix()を呼び出してエラー修正ロジックを追加します。 追加の強調表示を適用するために、注釈は空のメッセージでAnnotationHolder.createInfoAnnotation()を呼び出し、次にAnnotation.setTextAttributes()を呼び出して、テキスト属性を設定できます。



例: プロパティ言語のアノテーター



外部注釈


最後に、ユーザー言語がファイル検証に外部ツールを使用する場合、 ExternalAnnotatorインターフェースを実装し、 com.intellij.externalAnnotator



拡張com.intellij.externalAnnotator



で登録することにより、結果を提供できます。 ExternalAnnotatorを使用したバックライトの優先順位は最も低く、すべてのバックグラウンドプロセスが完了した後にのみ発生します。 外部注釈は、同じAnnotationHolderインターフェイスを使用して、外部ツールの出力を調整し、強調表示を表示します。



色設定ページ


プラグインは、ユーザーが特定の要素の色をカスタマイズできるようにする構成インターフェイスも提供できます。 これを行うには、 ColorSettingPageクラスのインスタンスを作成し、拡張ポイントcom.intellij.colorSettingsPage



登録する必要がcom.intellij.colorSettingsPage



ます。



例: プロパティのColorSettingsPage



「HTMLにエクスポート」機能は、エディターと同じ強調表示メカニズムを使用するため、SyntaxHighlighterの実装直後にユーザー言語で使用可能になります。



IntelliJ IDEAリファレンスシステム



ユーザー言語プログラム構造の実装で最も重要でわかりにくい部分の1つは、リンク解決です。 要素の使用場所(式の変数、メソッド呼び出しなど)から定義の場所(変数、メソッドなどの宣言)に進む機能。 これは、「宣言に移動」(Ctrl-BおよびCtrl-Click)などの多くのIDEA機能をサポートするため、および使用、名前変更、およびオートコンプリートを検索する場合に必要です。



参照として動作する必要がある各PSI要素は、 PsiReferenceインターフェースの対応する実装を返すように、PsiElement.getReference()メソッドをオーバーライドする必要があります。 このインターフェイスは、PsiElementクラス自体によって、または個別のインターフェイスとして実装できます。 要素に複数のリンクを含めることができる場合(クラスをリストする行など)、この場合、リンクの配列を返すPsiElement.getReferences()メソッドを実装する必要があります。



PsiReferenceインターフェイスのメインメソッドはresolve()で、リンクが指す要素を返すか、リンクが解決できない場合(たとえば、未定義のクラスを指す場合)にnullを返します。 反対のメソッドisReferenceTo()は、リンクが特定の要素を指しているかどうかを確認します。 後者のメソッドは、resolve()を呼び出し、結果を渡されたPSI要素と比較することで実装できますが、開発者は追加の最適化を適用できます。



例:プロパティプラグインのResourceBundleへのリンク



IDEAは、リンクサポートを実装するためのベースとして使用できる多くのインターフェイス、つまりPsiScopeProcessorインターフェイスとPsiElement.processDeclarations()メソッドを提供します。 これらのインターフェースには、ほとんどのユーザー言語には必要ない多くの複雑さがありますが(例えば、ジェネリック型の置換のサポート)、ユーザー言語がJavaコードを参照できる場合に必要です。 Javaとの相互運用性が必要ない場合、または他の理由がある場合、プラグインはリンク解決の標準実装をオーバーライドする場合があります。



参照の解決に使用される標準IDEAヘルパークラスは、次のコンポーネントで構成されています。



リンクが複数のターゲット要素を使用できるようにするPsiReferenceインターフェイスの拡張機能があります-PsiPolyVariantReference。 リンクターゲット要素はmultiResolve()メソッドによって返されます。 このタイプのリンクの「宣言に移動」アクションを使用すると、ナビゲーションに使用する要素を選択できます。 multiResolve()の実装は、最初の結果が見つかった後に検索を停止する代わりに、残りのターゲット要素の収集を続ける場合、PsiScopeProcessorに基づくことができます。



一方、IntelliJ IDEAには、参照コントリビューターおよび参照プロバイダーを介して参照システムを実装するアプローチがあります。

PsiReferenceContributorは各PsiElementをチェックし、ユーザーが設定した対応する説明に従って、このケースに登録されているリンクプロバイダーのオブジェクトを返します( )。 同様に、 PsiReferenceProviderは、PSIツリーの単一要素内のリンクを見つけるように設計されたクラスです。 PsiReferenceオブジェクトの配列を返します( )。

PsiReferenceProvider.getReferencesByElement()メソッドは、渡されたPsiElement要素に含まれるリンクのリスト(PsiReference)を返す必要があります。 この場合、1つのリンクのみが返されますが、一般的な場合は複数存在する可能性があり、各リンクには対応するtextRange(PSI要素のテキスト内のリンクの開始インデックスと終了インデックス)を含める必要があります。



参照コントリビューターは、適切な拡張ポイント-com.intellij.psi.referenceContributorのplugin.xmlファイルに登録する必要があります。

その後、PsiElement.getReferences()メソッドを実装するときに、彼の作業の結果を使用してリンクのリストを取得することができます。 各参照要素でこのコードを複製しないように、カスタムPsi要素の基本クラスを定義できます。

 public class MyASTWrapperPsiElement extends ASTWrapperPsiElement { public MyASTWrapperPsiElement(@NotNull ASTNode astNode) { super(astNode); } @Override public PsiReference getReference() { PsiReference[] references = getReferences(); return references.length == 0 ? null : references[0]; } @NotNull @Override public PsiReference[] getReferences() { //  ,   Contributor return ReferenceProvidersRegistry.getReferencesFromProviders(this); } }
      
      





コード補完



カスタム言語プラグインで使用できる自動補完には、単純なリンクアドオンとプロバイダーベースのアドオンの2つの主な種類があります。



シンプルなオートコンプリート


アドオンリストを作成するために、IDEAはカーソルの下のリンクまたはカーソルの下に配置されたダミー要素のいずれかでPsiReference.getVariants()メソッドを呼び出します。 このメソッドは、文字列、PSI要素、またはLookupElementクラスのインスタンスを含むオブジェクトの配列を返す必要があります。 返された配列でPsiElementのインスタンスが見つかった場合、対応するアイコンが追加のリストに表示されます。

getVariants()を実装する最も一般的な方法は、PsiReference.resolve()メソッドと同じツリートラバーサル関数を使用することですが、見つかったすべての定義を返します。



プロバイダーベースの自動補完


CompletionContributorインターフェースに基づくアドオンの実装により、コード補完の操作を最大限に制御できます。

Completion Contributorを使用する主なシナリオは、extend()メソッドを呼び出し、このアドオンオプションを適用できる「パターン」パラメーターに適切なコンテキストを渡すことで構成されます。



例:MANIFEST.MFファイル内のキーワードの自動補完のためのCompletionContributor



自動補完リストの項目は、 LookupElementインターフェイスのインスタンスによって表されます。 これらのオブジェクトは、通常LookupElementBuilderを使用して作成されます。 それぞれについて、次の属性を定義できます。



利用状況検索



IDEAの使用状況の検索アクションはマルチステッププロセスであり、その各ステップはプラグインからの参加を必要とします:拡張ポイントcom.intellij.lang.findUsagesProviderでのFindUsagesProviderの実装および登録の形式、およびプログラム構造(PsiNamedElementおよびPsiReferenceインターフェース)の実装の機能。



例:プロパティのFindUsagesProvider実装します。



この機能を実装するには、次の手順を実行する必要があります。



Find Usagesパネルの見出しに見つかった要素の名前を正しく表示するには、ElementDescriptionProviderインターフェイスの実装を提供する必要があります。 この場合、プロバイダーに渡されるElementDescriptionLocationオブジェクトは、実際のタイプUsageViewLongNameLocationである必要があります。



例:プロパティプラグインのElementDescriptionProvider



次の部分では、リファクタリング、フォーマットなど。



サイクルのすべての記事: 1、2、3、4、5、6、7



All Articles