IDEA's "Quick Definition Lookup" action is based on the same mechanism as "Go to Declaration", so it becomes automatically available for all references which can be resolved by the language plugin.
There are two main types of code completion that can be provided by custom language plugins: reference completion and contributor-based completion.
Reference completion is easier to implement, but supports only the basic completion action. Contributor-based completion provides more features, supports all three completion types (basic, smart and class name) and can be used, for example, to implement keyword completion.
To fill the completion list, IDEA calls
PsiReference.getVariants() either on the reference at the caret location or on a dummy reference that would be placed at the caret. This method needs to return an array of objects containing either strings, PsiElement instances or instances of the
LookupElement class. If a PsiElement instance is returned in the array, the completion list shows the icon for the element.
The most common way to implement getVariants() is to use the same function for walking up the tree as in PsiReference.resolve(), and a different implementation of PsiScopeProcessor which collects all declarations passed to its processDeclarations() method and returns them as an array for filling the completion list.
The Find Usages action in IDEA is a multi-step process, and each step of the process requires involvement from the custom language plugin. The language plugin participates in the Find Usages process by registering an implementation of FindUsagesProvider in the
com.intellij.lang.findUsagesProvider extension point, and through the PSI implementation (PsiNamedElement and PsiReference interfaces).
The steps of the Find Usages action are the following:
- Before the Find Usages action can be invoked, IDEA builds an index of words present in every file in the custom language. Using the WordsScanner implementation returned from
FindUsagesProvider.getWordsScanner(), IDEA loads the contents of every file and passes it to the words scanner, along with the words consumer. The words scanner breaks the text into words, defines the context for each word (code, comments or literals) and passes the word to the consumer. The simplest way to implement the words scanner is to use the DefaultWordsScanner implementation, passing to it the sets of lexer token types which are treated as identifiers, literals and comments. The default words scanner will use the lexer to break the text into tokens, and will handle breaking the text of comment and literal tokens into individual words.
- Once the element is located, IDEA calls FindUsagesProvider.canFindUsagesFor() to ask the plugin if the Find Usages action is applicable to the specific element.
- When showing the Find Usages dialog to the user, IDEA calls
FindUsagesProvider.getDescriptiveName()to determine how the element should be presented to the user.
- For every file containing the searched words, IDEA builds the PSI tree and recursively descends that tree. IDEA breaks the text of each element into words and scans them. If the element was indexed as an identifier, every word is checked to be a PsiReference resolving to the element the usages of which are searched. If the element was indexed as a comment or literal and the search in comments or literals is enabled, it checks if the word is equal to the name of the searched element.
- After the usages are collected, IDEA shows the results in the usages pane. The text shown for each found element is taken from the FindUsagesProvider.getNodeText() method.
to be continued