diff --git a/tutorials/custom_language_support/annotator.md b/tutorials/custom_language_support/annotator.md index c9445b2ee..4e823ac60 100644 --- a/tutorials/custom_language_support/annotator.md +++ b/tutorials/custom_language_support/annotator.md @@ -2,63 +2,31 @@ title: 7. Annotator --- -Annotator helps highlight and annotate any code based on specific rules. +An [Annotator](/reference_guide/custom_language_support/syntax_highlighting_and_error_highlighting.md#annotator) helps highlight and annotate any code based on specific rules. +This section adds annotation functionality to support the Simple language in the context of Java code. -### 7.1. Define an annotator - -In this tutorial we will annotate usages of our properties within Java code. -Let's consider a literal which starts with *"simple:"* as a usage of our property. +* bullet item +{:toc} +## 7.1. Define an Annotator +The `SimpleAnnotator` subclasses [`Annotator`](upsource:///platform/analysis-api/src/com/intellij/lang/annotation/Annotator.java). +Consider a literal string that starts with "simple:" as a prefix of a Simple language key. +It isn't part of the Simple language, but it is a useful convention for detecting Simple language keys embedded as string literals in other languages, like Java. +Annotate the `simple:key` literal expression, and differentiate between a well-formed vs. an unresolved property: ```java -package com.simpleplugin; - -import com.intellij.lang.annotation.*; -import com.intellij.openapi.editor.DefaultLanguageHighlighterColors; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.TextRange; -import com.intellij.psi.*; -import com.simpleplugin.psi.SimpleProperty; -import org.jetbrains.annotations.NotNull; - -import java.util.List; - -public class SimpleAnnotator implements Annotator { - @Override - public void annotate(@NotNull final PsiElement element, @NotNull AnnotationHolder holder) { - if (element instanceof PsiLiteralExpression) { - PsiLiteralExpression literalExpression = (PsiLiteralExpression) element; - String value = literalExpression.getValue() instanceof String ? (String) literalExpression.getValue() : null; - - if (value != null && value.startsWith("simple" + ":")) { - Project project = element.getProject(); - String key = value.substring(7); - List properties = SimpleUtil.findProperties(project, key); - if (properties.size() == 1) { - TextRange range = new TextRange(element.getTextRange().getStartOffset() + 8, - element.getTextRange().getEndOffset() - 1); - Annotation annotation = holder.createInfoAnnotation(range, null); - annotation.setTextAttributes(DefaultLanguageHighlighterColors.LINE_COMMENT); - } else if (properties.size() == 0) { - TextRange range = new TextRange(element.getTextRange().getStartOffset() + 8, - element.getTextRange().getEndOffset() - 1); - holder.createErrorAnnotation(range, "Unresolved property"); - } - } - } - } -} +{% include /code_samples/simple_language/src/main/java/com/intellij/sdk/language/SimpleAnnotator.java %} ``` -### 7.2. Register the annotator - +## 7.2. Register the Annotator +Using an extension point, register the Simple language annotator class with the IntelliJ Platform: ```xml - + + + ``` -### 7.3. Run the project - -Let's define the following Java file and check if the IDE resolves a property. - +## 7.3. Run the Project +As a test, define the following Java file containing a Simple language `prefix:value` pair: ```java public class Test { public static void main(String[] args) { @@ -67,9 +35,12 @@ public class Test { } ``` -![Annotator](img/annotator.png) +Open this Java file in an IDE Development Instance running the `simple_language` plugin to check if the IDE resolves a property: -If we type an undefined property name, it will annotate the code with a error. +![Annotator](img/annotator.png){:width="800px"} -![Unresolved property](img/unresolved_property.png) +If the property is an undefined name, the annotator will annotate the code with an error. +![Unresolved property](img/unresolved_property.png){:width="800px"} + +Try changing the Simple language [color settings](/tutorials/custom_language_support/syntax_highlighter_and_color_settings_page.md#run-the-project-1) to differentiate the annotation from the default language color settings. \ No newline at end of file diff --git a/tutorials/custom_language_support/code_style_settings.md b/tutorials/custom_language_support/code_style_settings.md index 5da2422ad..2c2105ef2 100644 --- a/tutorials/custom_language_support/code_style_settings.md +++ b/tutorials/custom_language_support/code_style_settings.md @@ -2,38 +2,49 @@ title: 16. Code Style Setting --- -Code style settings allow defining formatting options. A code style settings provider will create an instance of the settings, and also create an options page in order to edit them. In this example, we'll create a page that uses the default language code style settings, customised by a language code style settings provider. +[Code style settings](/reference_guide/custom_language_support/code_formatting.md#code-style-settings) enable defining formatting options. +A code style settings provider creates an instance of the settings, and also creates an options page in settings/preferences. +This example creates a settings/preferences page that uses the default language code style settings, customised by a language code style settings provider. -### 16.1. Define code style settings +* bullet list +{:toc} +## 16.1. Define Code Style Settings +Define a code style settings for Simple language by subclassing [`CustomCodeStyleSettings`](upsource:///platform/lang-api/src/com/intellij/psi/codeStyle/CustomCodeStyleSettings.java). ```java -{% include /code_samples/simple_language_plugin/src/com/simpleplugin/SimpleCodeStyleSettings.java %} +{% include /code_samples/simple_language/src/main/java/com/intellij/sdk/language/SimpleCodeStyleSettings.java %} ``` -### 16.2. Define code style settings provider - +## 16.2. Define Code Style Settings Provider +The code style settings provider gives the IntelliJ Platform a standard way to instantiate `CustomCodeStyleSettings` for the Simple language. +Define a code style settings provider for Simple language by subclassing [`CodeStyleSettingsProvider`](upsource:///platform/lang-api/src/com/intellij/psi/codeStyle/CodeStyleSettingsProvider.java). ```java -{% include /code_samples/simple_language_plugin/src/com/simpleplugin/SimpleCodeStyleSettingsProvider.java %} +{% include /code_samples/simple_language/src/main/java/com/intellij/sdk/language/SimpleCodeStyleSettingsProvider.java %} ``` -### 16.3. Register the code style settings provider - +## 16.3. Register the Code Style Settings Provider +The `SimpleCodeStyleSettingsProvider` implementation is registered with the IntelliJ Platform in `plugin.xml` using the `codeStyleSettingsProvider` extension point. ```xml - + + + ``` -### 16.4. Define language code style settings provider - +## 16.4. Define the Language Code Style Settings Provider +Define a code style settings provider for Simple language by subclassing [`LanguageCodeStyleSettingsProvider`](upsource:///platform/lang-api/src/com/intellij/psi/codeStyle/LanguageCodeStyleSettingsProvider.java), which provides common code style settings for a specific language. ```java {% include /code_samples/simple_language_plugin/src/com/simpleplugin/SimpleLanguageCodeStyleSettingsProvider.java %} ``` -### 16.5. Register the language code style settings provider - +## 16.5. Register the Language Code Style Settings Provider +The `SimpleLanguageCodeStyleSettingsProvider` implementation is registered with the IntelliJ Platform in `plugin.xml` using the `langCodeStyleSettingsProvider` extension point. ```xml - + + + ``` -### 16.6. Run the project +## 16.6. Run the project +In the IDE Development Instance, open the Simple language code formatting page: **Preferences/Settings \| Editor \| Code Style \| Simple**. ![Code Style Settings](img/code_style_settings.png) diff --git a/tutorials/custom_language_support/commenter.md b/tutorials/custom_language_support/commenter.md index 85549329e..0f8ad98ad 100644 --- a/tutorials/custom_language_support/commenter.md +++ b/tutorials/custom_language_support/commenter.md @@ -2,20 +2,32 @@ title: 17. Commenter --- -A commenter allows user to comment the code at the cursor or selected code automatically via corresponding actions. +A commenter enables the user to comment out line of code at the cursor or selected code automatically. +The [`Commenter`](upsource:///platform/core-api/src/com/intellij/lang/Commenter.java) defines support for "Comment with Line Comment" and "Comment with Block Comment" actions. -### 17.1. Define a commenter +* bullet list +{:toc} +## 17.1. Define a Commenter +The Simple language commenter subclasses `Commenter`. +This commenter defines the line comment prefix as "#". ```java -{% include /code_samples/simple_language_plugin/src/com/simpleplugin/SimpleCommenter.java %} +{% include /code_samples/simple_language/src/main/java/com/intellij/sdk/language/SimpleCommenter.java %} ``` -### 17.2. Register the commenter - +## 17.2. Register the Commenter +The `SimpleCommenter` implementation is registered with the IntelliJ Platform in `plugin.xml` using the `lang.commenter` extension point. ```xml - + + + ``` -### 17.3. Run the project +## 17.3. Run the Project +Open the example Simple Language [properties file ](/tutorials/custom_language_support/lexer_and_parser_definition.md#47-run-the-project) in the IDE Development Instance. +Place the cursor at the `website` line. +Select **Code \| Comment with Line Comment**. +The line is converted to a comment. +Select **Code \| Comment with Line Comment** again, and the comment is converted back to active code. -![Commenter](img/commenter.png) +![Commenter](img/commenter.png) {:width="800px"} diff --git a/tutorials/custom_language_support/completion_contributor.md b/tutorials/custom_language_support/completion_contributor.md index 55e7824a6..98c89b7c7 100644 --- a/tutorials/custom_language_support/completion_contributor.md +++ b/tutorials/custom_language_support/completion_contributor.md @@ -2,23 +2,31 @@ title: 9. Completion Contributor --- +Custom languages provide code completion using one of [two approaches](/reference_guide/custom_language_support/code_completion.md). +The Simple language plugin implements the less complex of the two methods, reference completion. -The easiest way to provide completion is to use a completion contributor. - -### 9.1. Define a completion contributor - -Let's provide custom completion for values in property files. +* bullet list +{:toc} +## 9.1. Define a Completion Contributor +For the purposes of this tutorial, the `simple_language` plugin provides custom completion for values in Simple language property files. +Create a completion contributor by subclassing [`CompletionContributor`](upsource:///platform/analysis-api/src/com/intellij/codeInsight/completion/CompletionContributor.java). +This rudimentary completion contributor always adds "Hello" to the results set, regardless of context: ```java -{% include /code_samples/simple_language_plugin/src/com/simpleplugin/SimpleCompletionContributor.java %} +{% include /code_samples/simple_language/src/main/java/com/intellij/sdk/language/SimpleCompletionContributor.java %} ``` -### 9.2. Register the completion contributor - +## 9.2. Register the Completion Contributor +The `SimpleCompletionContributor` implementation is registered with the IntelliJ Platform using the `completion.contributor` extension point. ```xml - + + + ``` -### 9.3. Run the project +## 9.3. Run the Project +Run the `simple_language` plugin in a Development Instance and open the [`test.simple`](/tutorials/custom_language_support/lexer_and_parser_definition.md#run-the-project) file. +Erase the property "English" and invoke [Basic Code Completion](https://www.jetbrains.com/help/idea/auto-completing-code.html#invoke-basic-completion). +The choice "Hello" is shown: -![Completion](img/completion.png) +![Completion](img/completion.png){:width="800px"} diff --git a/tutorials/custom_language_support/find_usages_provider.md b/tutorials/custom_language_support/find_usages_provider.md index e5fcd2ffc..1413a26c7 100644 --- a/tutorials/custom_language_support/find_usages_provider.md +++ b/tutorials/custom_language_support/find_usages_provider.md @@ -2,23 +2,30 @@ title: 11. Find Usages Provider --- -A find usage provider uses a word scanner to build an index of words present in every file. -A scanner breaks the text into words, defines the context for each word and passes it to the find usage provider. +A [find usage provider](/reference_guide/custom_language_support/find_usages.md) uses a word scanner to build an index of words in every file. +A scanner breaks the text into words, defines the context for each word, and passes it to the find usage provider. -### 11.1. Define a find usages provider +* bullet list +{:toc} +## 11.1. Define a Find Usages Provider +The `SimpleFindUsagesProvider` is implements [`FindUsagesProvider`](upsource:///platform/indexing-api/src/com/intellij/lang/findUsages/FindUsagesProvider.java). +Using the [`DefaultWordsScanner`](upsource:///platform/indexing-api/src/com/intellij/lang/cacheBuilder/DefaultWordsScanner.java) ensures the scanner implementation is thread-safe. +See the comments in `FindUsagesProvider` for more information. ```java -{% include /code_samples/simple_language_plugin/src/com/simpleplugin/SimpleFindUsagesProvider.java %} +{% include /code_samples/simple_language/src/main/java/com/intellij/sdk/language/SimpleFindUsagesProvider.java %} ``` -### 11.2. Register the find usages provider - +## 11.2. Register the Find Usages Provider +The `SimpleFindUsagesProvider` implementation is registered with the IntelliJ Platform using the `lang.findUsagesProvider` extension point. ```xml - + + + ``` -### 11.3. Run the project +## 11.3. Run the Project +Rebuild the project, and run `simple_language` in a Development Instance. +The IDE now supports [Find Usages](https://www.jetbrains.com/help/idea/find-highlight-usages.html) for any property with a reference: -Now we can call *Find Usages* for any property with a reference. - -![Find Usages](img/find_usages.png) +![Find Usages](img/find_usages.png){:width="800px"} diff --git a/tutorials/custom_language_support/folding_builder.md b/tutorials/custom_language_support/folding_builder.md index 4fd743ca1..d0c7fbab7 100644 --- a/tutorials/custom_language_support/folding_builder.md +++ b/tutorials/custom_language_support/folding_builder.md @@ -2,24 +2,40 @@ title: 12. Folding Builder --- -A folding builder helps you to fold the code regions and replace it with specific text. +A folding builder identifies the folding regions in the code. +In this step of the tutorial the folding builder is used to identify folding regions and replace the regions with specific text. +Rather than the usual practice of using a folding builder to collapse a class, method, or comments to fewer lines, the folding builder will replace Simple language keys with their corresponding values. -### 12.1. Define a folding builder +* bullet list +{:toc} -Let's replace usages of properties with its values by default. +## 12.1. Define a Folding Builder +The `SimpleFoldingBuilder` will replace usages of properties with their values by default. +Start by subclassing [`FoldingBuilderEx`](upsource:///community/platform/core-api/src/com/intellij/lang/folding/FoldingBuilderEx.java) +Note that `SimpleFoldingBuilder` also implements `DumbAware`, which means the class is allowed to run in dumb mode, when indices are in background update. + +The `buildFoldRegions()` method searches down a PSI tree from `root` to find all literal expressions containing the [simple prefix](/tutorials/custom_language_support/annotator.md#define-an-annotator) `simple:`. +The remainder of such a string is considered to contain a Simple language key, and so the text range is stored as a [`FoldingDescriptor`](upsource:///platform/core-api/src/com/intellij/lang/folding/FoldingDescriptor.java). + +The `getPlaceholderText()` method retrieves the Simple language value corresponding to the key associated with the (ASTNode) provided. +The IntelliJ Platform uses the value to substitute for the key when the code is folded. ```java -{% include /code_samples/simple_language_plugin/src/com/simpleplugin/SimpleFoldingBuilder.java %} +{% include /code_samples/simple_language/src/main/java/com/intellij/sdk/language/SimpleFoldingBuilder.java %} ``` -### 12.2. Register the folding builder - +## 12.2. Register the Folding Builder +The `SimpleFoldingBuilder` implementation is registered with the IntelliJ Platform using the `lang.foldingBuilder` extension point. ```xml - + + + ``` -### 12.3. Run the project +## 12.3. Run the Project +Rebuild the project, and run `simple_language` in a Development Instance. +Now when a Java file is opened in the Editor it shows the property's value instead of the key. +This is because `SimpleFoldingBuilder.isCollapsedByDefault()` always returns `true`. +Try using **Code \| Folding \| Expand All** to show the key rather than the value. -Now when we open a Java file, it shows the property's value instead of the key. - -![Folding](img/folding.png) +![Folding](img/folding.png){:width="800px"} diff --git a/tutorials/custom_language_support/formatter.md b/tutorials/custom_language_support/formatter.md index 1eb9067b2..529cba642 100644 --- a/tutorials/custom_language_support/formatter.md +++ b/tutorials/custom_language_support/formatter.md @@ -2,33 +2,38 @@ title: 15. Formatter --- -*A formatter allows to reformat the code automatically based on code style settings.* +The IntelliJ Platform includes a powerful framework for implementing formatting for custom languages. +A [formatter](/reference_guide/custom_language_support/code_formatting.md) enables reformatting code automatically based on code style settings. +The formatter controls spaces, indents, wrap, and alignment. -### 15.1. Define a block - -The formatter uses the blocks to receive formatting rules for each PSI element. -Our goal is to cover each PSI element with such block. Since each block builds own children blocks we can generate extra blocks or skip any PSI elements. +* bullet list +{:toc} +## 15.1. Define a Block +The formatting model builds represents the formatting structure of a file as a tree of [`Block`](upsource:///platform/lang-api/src/com/intellij/formatting/Block.java) objects, with associated indent, wrap, alignment and spacing setting +The goal is to cover each PSI element with such a block. +Since each block builds its children's blocks, it can generate extra blocks or skip any PSI elements. ```java -{% include /code_samples/simple_language_plugin/src/com/simpleplugin/SimpleBlock.java %} +{% include /code_samples/simple_language/src/main/java/com/intellij/sdk/language/SimpleBlock.java %} ``` -### 15.2. Define a formatting model builder - -Let's define a formatter which removes extra spaces except the single ones around the property separator. - +## 15.2. Define a Formatting Model Builder +Define a formatter which removes extra spaces except the single spaces around the property separator. +For example, reformat `foo = bar` to `foo = bar`. ```java -{% include /code_samples/simple_language_plugin/src/com/simpleplugin/SimpleFormattingModelBuilder.java %} +{% include /code_samples/simple_language/src/main/java/com/intellij/sdk/language/SimpleFormattingModelBuilder.java %} ``` -### 15.3. Register the formatter - +## 15.3. Register the Formatter +The `SimpleFormattingModelBuilder` implementation is registered with the IntelliJ Platform in `plugin.xml` using the `lang.formatter` extension point. ```xml - + + + ``` -### 15.4. Run the project +## 15.4. Run the Project +Add some extra spaces around the `=` separator between `language` and `English`. +Reformat the code by selecting **Code \| Show Reformat File Dialog** and choose **Run**. -Now add some extra spaces and reformat the code via **Code \| Reformat Code**. - -![Formatter](img/formatter.png) +![Formatter](img/formatter.png){:width="800px"} diff --git a/tutorials/custom_language_support/go_to_symbol_contributor.md b/tutorials/custom_language_support/go_to_symbol_contributor.md index d5f7741c9..8bc181dd6 100644 --- a/tutorials/custom_language_support/go_to_symbol_contributor.md +++ b/tutorials/custom_language_support/go_to_symbol_contributor.md @@ -2,14 +2,15 @@ title: 13. Go To Symbol Contributor --- -*A go to symbol contributor helps user to navigate to any PSI element by its name.* +A go to symbol contributor helps the user to navigate to any PSI element by its name. -### 13.1. Define helper method for generated PSI elements - -To specify how a PSI element looks like in the *Go To Symbol* popup window, *Structure* tool window or another components, it should implement *getPresentation* method. - -This means we need to define this method in our utility *com.simpleplugin.psi.impl.SimplePsiImplUtil* and regenerate the parser and PSI classes. +* bullet list +{:toc} +## 13.1. Define a Helper Method for Generated PSI Elements +To specify how a PSI element looks like in the *Go To Symbol* popup window, *Structure* tool window or another components, it should implement `getPresentation()`. +This means defining this method in the utility `com.intellij.sdk.language.psi.impl.SimplePsiImplUtil` and regenerate the parser and PSI classes. +Add the following method to `SimplePsiImplUtil`: ```java public static ItemPresentation getPresentation(final SimpleProperty element) { return new ItemPresentation() { @@ -34,29 +35,31 @@ public static ItemPresentation getPresentation(final SimpleProperty element) { } ``` -### 13.2. Update grammar and regenerate the parser - +## 13.2. Update Grammar and Regenerate the Parser +Now add the `SimplePsiImplUtil.getPresentation()` to the `property` definition in the `Simple.bnf` grammar file by replacing the `property` definition with the lines below. +Don't forget to regenerate the parser after updating the file! +Right click on the `Simple.bnf` file and select **Generate Parser Code**. ```java -property ::= (KEY? SEPARATOR VALUE?) | KEY {mixin="com.simpleplugin.psi.impl.SimpleNamedElementImpl" - implements="com.simpleplugin.psi.SimpleNamedElement" methods=[getKey getValue getName setName getNameIdentifier getPresentation]} +property ::= (KEY? SEPARATOR VALUE?) | KEY {mixin="com.intellij.sdk.language.psi.impl.SimpleNamedElementImpl" + implements="com.intellij.sdk.language.psi.SimpleNamedElement" methods=[getKey getValue getName setName getNameIdentifier getPresentation]} ``` -Regenerate the parser by right clicking on the `Simple.bnf` file and selecting _Generate Parser Code_. - -### 13.3. Define a go to symbol contributor - +## 13.3. Define a Go to Symbol Contributor +To enable the `simple_language` plugin to contribute items to "Navigate Class|File|Symbol" lists, subclass [`ChooseByNameContributor`](upsource:///platform/lang-api/src/com/intellij/navigation/ChooseByNameContributor.java) to create `SimpleChooseNameContributor`: ```java -{% include /code_samples/simple_language_plugin/src/com/simpleplugin/SimpleChooseByNameContributor.java %} +{% include /code_samples/simple_language/src/main/java/com/intellij/sdk/language/SimpleChooseByNameContributor.java %} ``` -### 13.4. Register the go to symbol contributor - +## 13.4. Register the go to symbol contributor +The `SimpleChooseByNameContributor` implementation is registered with the IntelliJ Platform using the `gotoSymbolContributor` extension point. ```xml - + + + ``` -### 13.5. Run the project +## 13.5. Run the project +Rebuild the project, and run `simple_language` in a Development Instance. +The IDE now supports navigating to a property definition by name pattern via **Navigate \| Symbol** action. -Now we can navigate to a property definition by name pattern via **Navigate \| Symbol** action. - -![Go To Symbol](img/go_to_symbol.png) +![Go To Symbol](img/go_to_symbol.png){:width="800px"} diff --git a/tutorials/custom_language_support/grammar_and_parser.md b/tutorials/custom_language_support/grammar_and_parser.md index 12f6ec70c..7a3454854 100644 --- a/tutorials/custom_language_support/grammar_and_parser.md +++ b/tutorials/custom_language_support/grammar_and_parser.md @@ -2,40 +2,42 @@ title: 3. Grammar and Parser --- -### 3.1. Define a token type - -Create a file in the `com.simpleplugin.psi` package. +In order for the IntelliJ Platform to parse a Simple language file, [tokens and elements](/reference_guide/custom_language_support/implementing_parser_and_psi.md) must be defined based on [`IElementType`](upsource:///platform/core-api/src/com/intellij/psi/tree/IElementType.java). +The Simple language grammar must also be defined in order to generate a parser. + +* bullet item +{:toc} +## 3.1. Define a Token Type +Create `SimpleTokenType` in the `com.intellij.sdk.language.psi` package (see the `simple_language` code sample) by subclassing `IElementType`. ```java -{% include /code_samples/simple_language_plugin/src/com/simpleplugin/psi/SimpleTokenType.java %} +{% include /code_samples/simple_language/src/main/java/com/intellij/sdk/language/psi/SimpleTokenType.java %} ``` -### 3.2. Define an element type - -Create a file in the `com.simpleplugin.psi` package. - +## 3.2. Define an Element Type +Create the `SimpleElementType` in the `com.intellij.sdk.language.psi` package by subclassing `IElementType`. ```java -{% include /code_samples/simple_language_plugin/src/com/simpleplugin/psi/SimpleElementType.java %} +{% include /code_samples/simple_language/src/main/java/com/intellij/sdk/language/psi/SimpleElementType.java %} ``` -### 3.3. Define grammar - -Define a grammar for the properties language with */com/simpleplugin/Simple.bnf* file. - +## 3.3. Define the Grammar +Define a grammar for the Simple language in the `com/intellij/sdk/language/Simple.bnf` file. ```java { - parserClass="com.simpleplugin.parser.SimpleParser" + parserClass="com.intellij.sdk.language.parser.SimpleParser" extends="com.intellij.extapi.psi.ASTWrapperPsiElement" psiClassPrefix="Simple" psiImplClassSuffix="Impl" - psiPackage="com.simpleplugin.psi" - psiImplPackage="com.simpleplugin.psi.impl" + psiPackage="com.intellij.sdk.language.psi" + psiImplPackage="com.intellij.sdk.language.psi.impl" - elementTypeHolderClass="com.simpleplugin.psi.SimpleTypes" - elementTypeClass="com.simpleplugin.psi.SimpleElementType" - tokenTypeClass="com.simpleplugin.psi.SimpleTokenType" + elementTypeHolderClass="com.intellij.sdk.language.psi.SimpleTypes" + elementTypeClass="com.intellij.sdk.language.psi.SimpleElementType" + tokenTypeClass="com.intellij.sdk.language.psi.SimpleTokenType" + + psiImplUtilClass="com.intellij.sdk.language.psi.impl.SimplePsiImplUtil" } simpleFile ::= item_* @@ -45,18 +47,17 @@ private item_ ::= (property|COMMENT|CRLF) property ::= (KEY? SEPARATOR VALUE?) | KEY ``` -As you see a properties file can contain properties, comments and line breaks. +As shown, a properties file can contain properties, comments, and line breaks. -The grammar defines how flexible the support for a language can be. -We specified that a property may have or may not have key and value. -This lets the IDE still recognise incorrectly defined properties and provide corresponding code analysis and quick-fixes. +The grammar defines the flexibility of the support for a language. +The above grammar specifies that a property may have or may not have key and value. +This allows the IntelliJ Platform to still recognise incorrectly defined properties and provide corresponding code analysis and quick-fixes. -Note that the `SimpleTypes` class in the `elementTypeHolderClass` field above specifies the name of a class that gets generated from the grammar, it doesn't exist at this point. +Note that the `SimpleTypes` class in the `elementTypeHolderClass` field above specifies the name of a class that gets generated from the grammar; it doesn't exist at this point. -### 3.4. Generate a parser - -Now when the grammar is defined we can generate a parser with PSI classes via *Generate Parser Code* from the context menu on *Simple.bnf* file. -This will generate a parser and PSI elements in *gen* folder. +## 3.4. Generate a Parser +Now that the grammar is defined, generate a parser with PSI classes via **Generate Parser Code** from the context menu on the *Simple.bnf* file. +This will generate a parser and PSI elements in the `/src/main/gen` folder of the project. Mark this folder as *Generated Sources Root* and make sure everything is compiled without errors. -![Parser](img/generated_parser.png) +![Parser](img/generated_parser.png){:width="800px"} diff --git a/tutorials/custom_language_support/img/annotator.png b/tutorials/custom_language_support/img/annotator.png index 4bc3e0719..ba5cd8a3d 100644 Binary files a/tutorials/custom_language_support/img/annotator.png and b/tutorials/custom_language_support/img/annotator.png differ diff --git a/tutorials/custom_language_support/img/code_style_settings.png b/tutorials/custom_language_support/img/code_style_settings.png index 26ba38e85..9b8114f90 100644 Binary files a/tutorials/custom_language_support/img/code_style_settings.png and b/tutorials/custom_language_support/img/code_style_settings.png differ diff --git a/tutorials/custom_language_support/img/color_settings_page.png b/tutorials/custom_language_support/img/color_settings_page.png index 836b0a6cf..63418c177 100644 Binary files a/tutorials/custom_language_support/img/color_settings_page.png and b/tutorials/custom_language_support/img/color_settings_page.png differ diff --git a/tutorials/custom_language_support/img/commenter.png b/tutorials/custom_language_support/img/commenter.png index 895c51905..46a98e46b 100644 Binary files a/tutorials/custom_language_support/img/commenter.png and b/tutorials/custom_language_support/img/commenter.png differ diff --git a/tutorials/custom_language_support/img/completion.png b/tutorials/custom_language_support/img/completion.png index 0d9d9689e..8bbea4af8 100644 Binary files a/tutorials/custom_language_support/img/completion.png and b/tutorials/custom_language_support/img/completion.png differ diff --git a/tutorials/custom_language_support/img/file_type_factory.png b/tutorials/custom_language_support/img/file_type_factory.png index 755e40580..359748c69 100644 Binary files a/tutorials/custom_language_support/img/file_type_factory.png and b/tutorials/custom_language_support/img/file_type_factory.png differ diff --git a/tutorials/custom_language_support/img/find_usages.png b/tutorials/custom_language_support/img/find_usages.png index 0c2155872..17e98392a 100644 Binary files a/tutorials/custom_language_support/img/find_usages.png and b/tutorials/custom_language_support/img/find_usages.png differ diff --git a/tutorials/custom_language_support/img/folding.png b/tutorials/custom_language_support/img/folding.png index 332d56f6e..7f242dc7a 100644 Binary files a/tutorials/custom_language_support/img/folding.png and b/tutorials/custom_language_support/img/folding.png differ diff --git a/tutorials/custom_language_support/img/formatter.png b/tutorials/custom_language_support/img/formatter.png index 2b3e5bc85..4d06ed2dd 100644 Binary files a/tutorials/custom_language_support/img/formatter.png and b/tutorials/custom_language_support/img/formatter.png differ diff --git a/tutorials/custom_language_support/img/generated_parser.png b/tutorials/custom_language_support/img/generated_parser.png index 3def5db2d..56229801c 100644 Binary files a/tutorials/custom_language_support/img/generated_parser.png and b/tutorials/custom_language_support/img/generated_parser.png differ diff --git a/tutorials/custom_language_support/img/go_to_symbol.png b/tutorials/custom_language_support/img/go_to_symbol.png index d46f69ad7..f6eeae69c 100644 Binary files a/tutorials/custom_language_support/img/go_to_symbol.png and b/tutorials/custom_language_support/img/go_to_symbol.png differ diff --git a/tutorials/custom_language_support/img/in_place_rename.png b/tutorials/custom_language_support/img/in_place_rename.png index 795968e62..d3179f05e 100644 Binary files a/tutorials/custom_language_support/img/in_place_rename.png and b/tutorials/custom_language_support/img/in_place_rename.png differ diff --git a/tutorials/custom_language_support/img/line_marker.png b/tutorials/custom_language_support/img/line_marker.png index 3bb3cb544..a96003e34 100644 Binary files a/tutorials/custom_language_support/img/line_marker.png and b/tutorials/custom_language_support/img/line_marker.png differ diff --git a/tutorials/custom_language_support/img/line_marker_location.png b/tutorials/custom_language_support/img/line_marker_location.png new file mode 100644 index 000000000..ba29bacee Binary files /dev/null and b/tutorials/custom_language_support/img/line_marker_location.png differ diff --git a/tutorials/custom_language_support/img/new_property.png b/tutorials/custom_language_support/img/new_property.png new file mode 100644 index 000000000..0cd68df96 Binary files /dev/null and b/tutorials/custom_language_support/img/new_property.png differ diff --git a/tutorials/custom_language_support/img/psi_elements.png b/tutorials/custom_language_support/img/psi_elements.png index 475162c54..588cbf34a 100644 Binary files a/tutorials/custom_language_support/img/psi_elements.png and b/tutorials/custom_language_support/img/psi_elements.png differ diff --git a/tutorials/custom_language_support/img/quick_fix.png b/tutorials/custom_language_support/img/quick_fix.png index a305d05c4..2637f1eed 100644 Binary files a/tutorials/custom_language_support/img/quick_fix.png and b/tutorials/custom_language_support/img/quick_fix.png differ diff --git a/tutorials/custom_language_support/img/reference_contributor.png b/tutorials/custom_language_support/img/reference_contributor.png index 05c9850bc..87d368920 100644 Binary files a/tutorials/custom_language_support/img/reference_contributor.png and b/tutorials/custom_language_support/img/reference_contributor.png differ diff --git a/tutorials/custom_language_support/img/rename.png b/tutorials/custom_language_support/img/rename.png index 5f0f41230..3904141eb 100644 Binary files a/tutorials/custom_language_support/img/rename.png and b/tutorials/custom_language_support/img/rename.png differ diff --git a/tutorials/custom_language_support/img/simple_named_element.png b/tutorials/custom_language_support/img/simple_named_element.png new file mode 100644 index 000000000..8c40dca2f Binary files /dev/null and b/tutorials/custom_language_support/img/simple_named_element.png differ diff --git a/tutorials/custom_language_support/img/structure_view.png b/tutorials/custom_language_support/img/structure_view.png index d9328a82f..783d7cec8 100644 Binary files a/tutorials/custom_language_support/img/structure_view.png and b/tutorials/custom_language_support/img/structure_view.png differ diff --git a/tutorials/custom_language_support/img/syntax_highlighter.png b/tutorials/custom_language_support/img/syntax_highlighter.png index cc092efcd..15811d3a5 100644 Binary files a/tutorials/custom_language_support/img/syntax_highlighter.png and b/tutorials/custom_language_support/img/syntax_highlighter.png differ diff --git a/tutorials/custom_language_support/img/unresolved_property.png b/tutorials/custom_language_support/img/unresolved_property.png index c8d69fc8d..a40da4184 100644 Binary files a/tutorials/custom_language_support/img/unresolved_property.png and b/tutorials/custom_language_support/img/unresolved_property.png differ diff --git a/tutorials/custom_language_support/language_and_filetype.md b/tutorials/custom_language_support/language_and_filetype.md index 31a6e766a..9a90a9efa 100644 --- a/tutorials/custom_language_support/language_and_filetype.md +++ b/tutorials/custom_language_support/language_and_filetype.md @@ -2,63 +2,66 @@ title: 2. Language and File Type --- +The IntelliJ Platform [determines file type](/reference_guide/custom_language_support/registering_file_type.md) by examining the name of a file. +Each language has [Language](upsource:///platform/core-api/src/com/intellij/lang/Language.java) and [LanguageFileType](upsource:///platform/core-api/src/com/intellij/openapi/fileTypes/LanguageFileType.java) objects defining the language. +The `LanguageFileType` is registered with the IntelliJ Platform in the plugin configuration file. -### 2.1. Define a language - -Note the case of the name of the language - `Simple`. +* bullet item +{:toc} +## 2.1. Define the Language +The language implemented in this tutorial is named "Simple" - note the case of the name. +The `SimpleLanguage` class is defined in the `com.intellij.sdk.language` package of the `simple_language` code sample: ```java -{% include /code_samples/simple_language_plugin/src/com/simpleplugin/SimpleLanguage.java %} +{% include /code_samples/simple_language/src/main/java/com/intellij/sdk/language/SimpleLanguage.java %} ``` -### 2.2. Define an icon - -Copy the -[icon](https://raw.githubusercontent.com/JetBrains/intellij-sdk-docs/master/code_samples/simple_language_plugin/src/com/simpleplugin/icons/jar-gray.png) -to **com.simpleplugin.icons** package. - +## 2.2. Define an Icon +The [icon](https://raw.githubusercontent.com/JetBrains/intellij-sdk-docs/master/code_samples/simple_language_plugin/src/com/simpleplugin/icons/jar-gray.png) for the Simple language is defined by the `SimpleIcons` class. +There is nothing uniquely Simple language-specific about [defining the icon](/reference_guide/work_with_icons_and_images.md) itself. +The definition follows a pattern similar to defining, e.g., `SdkIcons`. ```java -{% include /code_samples/simple_language_plugin/src/com/simpleplugin/SimpleIcons.java %} +{% include /code_samples/simple_language/src/main/java/com/intellij/sdk/language/SimpleIcons.java %} ``` -### 2.3. Define a file type - +## 2.3. Define a FileType +The Simple language file type is defined by subclassing `LanguageFileType`: ```java -{% include /code_samples/simple_language_plugin/src/com/simpleplugin/SimpleFileType.java %} +{% include /code_samples/simple_language/src/main/java/com/intellij/sdk/language/SimpleFileType.java %} ``` -### 2.4. Define a file type factory - -> **NOTE** When targeting 2019.2 or later only, please see [2.5.B](#b-register-file-type-20192-or-later) - -```java -{% include /code_samples/simple_language_plugin/src/com/simpleplugin/SimpleFileTypeFactory.java %} -``` - -### 2.5. Register the file type factory - -In plugin.xml add: - +## 2.4. Register the FileType Directly +Direct registration is necessary when targeting version 2019.2 (and later) of the IntelliJ Platform. +No `FileTypeFactory` is required. +Instead, the file type is registered of file type is done via the `com.intellij.fileType` extension point in `plugin.xml`: ```xml - + ``` -### 2.5.B. Register file type (2019.2 or later) +Skip to [section 2.6](#26-run-the-project). -When matching via file extension, pattern or exact file name, registration of file type should be done via `com.intellij.fileType` extension point instead of implementing dedicated `FileTypeFactory`. +## 2.5. Register the FileType Using a Factory +This pattern is necessary when targeting versions of the IntelliJ Platform prior to 2019.2 +### 2.5.1 Define a FileType Factory +First, define `SimpleFileTypeFactory` as a subclass of `FileTypeFactory`. +```java +{% include /code_samples/simple_language/src/main/java/com/intellij/sdk/language/SimpleFileTypeFactory.java %} +``` + +### 2.5.2 Register the FileType Factory +The `FileTypeFactory` is registered with the IntelliJ Platform using the `com.intellij.openapi.fileTypes.FileTypeFactory` extension point in `plugin.xml`. ```xml - + ``` -### 2.6. Run the project +## 2.6. Run the Project +Create an empty file with the extension `\*.simple` and IntelliJ IDEA will automatically associate it with our language. +Note the appearance of the Simple language file icon next to the `test.simple` file in the **Project Tool Window** and in the editor tab for the file. -Create a file with extension *.simple* -and IntelliJ IDEA will automatically associate it with our language. - -![File Type Factory](img/file_type_factory.png) +![File Type Factory](img/file_type_factory.png){:width="800px"} diff --git a/tutorials/custom_language_support/lexer_and_parser_definition.md b/tutorials/custom_language_support/lexer_and_parser_definition.md index 2795ce55a..79e73a815 100644 --- a/tutorials/custom_language_support/lexer_and_parser_definition.md +++ b/tutorials/custom_language_support/lexer_and_parser_definition.md @@ -2,57 +2,61 @@ title: 4. Lexer and Parser Definition --- -The lexer defines how the contents of a file is broken into tokens. +The lexical analyzer defines how the [contents of a file are broken into tokens](/reference_guide/custom_language_support/implementing_lexer.md), which is the basis for supporting custom language features. The easiest way to create a lexer is to use [JFlex](https://jflex.de/) -### 4.1. Define a lexer - -Define */com/simpleplugin/Simple.flex* file with rules for our lexer. +* bullet item +{:toc} +## 4.1. Define a Lexer +Define a `Simple.flex` file with rules for the Simple language lexer, as demonstrated in `com.intellij.sdk.language.Simple.flex`. ```java -{% include /code_samples/simple_language_plugin/src/com/simpleplugin/Simple.flex %} +{% include /code_samples/simple_language/src/main/java/com/intellij/sdk/language/Simple.flex %} ``` -### 4.2. Generate a lexer class +## 4.2. Generate a Lexer Class +Now generate a lexer class via **JFlex Generator** from the context menu on `Simple.flex` file. -Now we can generate a lexer class via *JFlex Generator* from the context menu on `Simple.flex` file. The Grammar-Kit plugin uses JFlex lexer generation. -If you run it for the first time, it offers you to choose a folder to download the JFlex library and skeleton to. -Choose the project root directory. +When run for the first time, it prompts for a destination folder to download the JFlex library and skeleton. +Choose the project root directory, for example `code_samples/simple_language`. -After that, the IDE generates the lexer: *com.simpleplugin.SimpleLexer*. +After that, the IDE generates the lexer under the `gen` directory, for example in `simple_language/src/main/gen/com/intellij/sdk/language/SimpleLexer`. -### 4.3. Define an adapter +See [Implementing Lexer](/reference_guide/custom_language_support/implementing_lexer.md) for more information about using _JFlex_ with the IntelliJ Platform. +## 4.3. Define a Lexer Adapter +The JFlex lexer needs to be adapted to the IntelliJ Platform Lexer API. +This is done by subclassing [`FlexAdapter`](upsource:///platform/core-api/src/com/intellij/lexer/FlexAdapter.java). ```java -{% include /code_samples/simple_language_plugin/src/com/simpleplugin/SimpleLexerAdapter.java %} +{% include /code_samples/simple_language/src/main/java/com/intellij/sdk/language/SimpleLexerAdapter.java %} ``` -### 4.4. Define a root file - -Create the class in the `com.simpleplugin.psi` namespace. - +## 4.4. Define a Root File +The `SimpleFile` implementation is the top-level node of the [tree of `PsiElements`](/reference_guide/custom_language_support/implementing_parser_and_psi.md) for a Simple language file. ```java -{% include /code_samples/simple_language_plugin/src/com/simpleplugin/psi/SimpleFile.java %} +{% include /code_samples/simple_language/src/main/java/com/intellij/sdk/language/psi/SimpleFile.java %} ``` -### 4.5. Define a parser definition - +## 4.5. Define a Parser +The Simple language parser is defined by subclassing [`ParserDefinition`](upsource:///platform/core-api/src/com/intellij/lang/ParserDefinition.java). ```java -{% include /code_samples/simple_language_plugin/src/com/simpleplugin/SimpleParserDefinition.java %} +{% include /code_samples/simple_language/src/main/java/com/intellij/sdk/language/SimpleParserDefinition.java %} ``` -### 4.6. Register the parser definition - +## 4.6. Register the Parser Definition +Registering the parser definition in the `plugin.xml` file makes it available to the IntelliJ Platform. +The `lang.parserDefinition` extension point is used for registration. +For example, see `simple_language/src/main/resources/META-INF/plugin.xml`. ```xml - + + + ``` -### 4.7. Run the project - -Create a properties file with the following content: - -``` +## 4.7. Run the Project +With the `simple_language` plugin loaded in a Development Instance, create a `test.simple` properties file with the following content: +```text # You are reading the ".properties" entry. ! The exclamation mark can also mark text as comments. website = http://en.wikipedia.org/ @@ -69,4 +73,4 @@ tab : \u0009 Now open the *PsiViewer* tool window and check how the lexer breaks the content of the file into tokens, and the parser parsed the tokens into PSI elements. -![PSI Elements](img/psi_elements.png) +![PSI Elements](img/psi_elements.png){:width="900px"} diff --git a/tutorials/custom_language_support/line_marker_provider.md b/tutorials/custom_language_support/line_marker_provider.md index d5abb506b..a4e9fc7c6 100644 --- a/tutorials/custom_language_support/line_marker_provider.md +++ b/tutorials/custom_language_support/line_marker_provider.md @@ -2,31 +2,37 @@ title: 8. Line Marker Provider --- -Line markers help to annotate any code with icons on the gutter. -These icons may provide navigation to related code. +Line markers help annotate code with icons on the gutter. +These markers can provide navigation targets to related code. -### 8.1. Define a line marker provider +* bullet list +{:toc} -Let's annotate usages of our properties within Java code and provide navigation to the definition of these properties. +## 8.1. Define a Line Marker Provider +A line marker provider annotates usages of Simple language properties within Java code and provides navigation to the definition of these properties. +The visual marker will be a Simple language icon in the gutter of the Editor window. +The Simple language marker provider is subclassed from [`RelatedItemLineMarkerProvider`](upsource:///platform/lang-api/src/com/intellij/codeInsight/daemon/RelatedItemLineMarkerProvider.java). +For this example, override the `collectNavigationMarkers()` method to collect usage of a Simple language [key and separators](/tutorials/custom_language_support/language_and_filetype.md#define-the-language): ```java -{% include /code_samples/simple_language_plugin/src/com/simpleplugin/SimpleLineMarkerProvider.java %} +{% include /code_samples/simple_language/src/main/java/com/intellij/sdk/language/SimpleLineMarkerProvider.java %} ``` -## More technical details for implementers - -* Please return line marker info for exact element you were asked for. - For example, do not return class marker info if `getLineMarkerInfo()` was called for a method. - -* Please return relevant line marker info for as small element as possible. - For example, do not return method marker for [`PsiMethod`](upsource:///java/java-psi-api/src/com/intellij/psi/PsiMethod.java). Instead, return it for the [`PsiIdentifier`](upsource:///java/java-psi-api/src/com/intellij/psi/PsiIdentifier.java) which is a name of this method. - -### Even more technical details: - -What happens when `LineMarkerProvider` returns something for too big PsiElement? +## 8.2. Best Practices for Implementing Line Marker Providers +This section addresses important details about implementing a marker provider. +The `collectNavigationMarkers()` method should: +* Only return line marker information consistent with the element passed into the method. + For example, do not return a _class_ marker if `getLineMarkerInfo()` was called with an element that corresponds to a _method_. +* Return line marker information for the appropriate element at the correct scope of the PSI tree. + For example, do not return method marker for [`PsiMethod`](upsource:///java/java-psi-api/src/com/intellij/psi/PsiMethod.java). + Instead, return it for the [`PsiIdentifier`](upsource:///java/java-psi-api/src/com/intellij/psi/PsiIdentifier.java) which contains the name of the method. + +![Line Marker Location](img/line_marker_location.png){:width="900px"} +What happens when a `LineMarkerProvider` returns marker information for a `PsiElement` that is a higher node in the PSI tree? +For example, if `MyWrongLineMarkerProvider()` erroneously returns a `PsiMethod` instead of a `PsiIdentifier` element: ```java -public class MyLineMarkerProvider implements LineMarkerProvider { +public class MyWrongLineMarkerProvider implements LineMarkerProvider { public LineMarkerInfo getLineMarkerInfo(@NotNull PsiElement element) { if (element instanceof PsiMethod) return new LineMarkerInfo(element, ...); return null; @@ -34,25 +40,20 @@ public class MyLineMarkerProvider implements LineMarkerProvider { } ``` -Inspection (specifically, [`LineMarkersPass`](upsource:///platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/LineMarkersPass.java)) for performance reasons queries all [`LineMarkerProviders`](upsource:///platform/lang-api/src/com/intellij/codeInsight/daemon/LineMarkerProviders.java) in two passes: +The consequences of the `MyWrongLineMarkerProvider()` implementation have to do with how the IntelliJ Platform performs inspections. +For performance reasons, inspection, and specifically the [`LineMarkersPass`](upsource:///platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/LineMarkersPass.java) queries all [`LineMarkerProviders`](upsource:///platform/lang-api/src/com/intellij/codeInsight/daemon/LineMarkerProviders.java) in two phases: +* The first pass is for all elements visible in the Editor window, +* The second pass is for the rest of the elements in the file. - * first pass for all elements in visible area - - * second pass for all the rest elements - -If providers return nothing for either area, its line markers are cleared. -So if e.g. a method is half-visible (its name is visible but part of its body isn't) and -some poorly written [`LineMarkerProvider`](upsource:///platform/lang-api/src/com/intellij/codeInsight/daemon/LineMarkerProvider.java) returned info for the `PsiMethod` instead of `PsiIdentifier` then: - - * the first pass removes line marker info because whole `PsiMethod` is not visible. - - * the second pass tries to add line marker info back because `LineMarkerProvider` is called for the `PsiMethod` at last. - -As a result, line marker icon would blink annoyingly. -To fix this, rewrite `LineMarkerProvider` to return info for `PsiIdentifier` instead of `PsiMethod`: +If providers return nothing for either area, the line markers are cleared. +However, if a method like `actionPerformed()` is not completely visible in the Editor window (as shown in the image above,) and `MyWrongLineMarkerProvider()` returns marker info for the `PsiMethod` instead of `PsiIdentifier`, then: +* The first pass removes line marker info because whole `PsiMethod` isn't visible. +* The second pass tries to add a line marker because `MyWrongLineMarkerProvider()` is called for the `PsiMethod`. +As a result, _the line marker icon would blink annoyingly_. +To fix this problem, rewrite `MyWrongLineMarkerProvider` to return info for `PsiIdentifier` instead of `PsiMethod` as shown below: ```java -public class MyLineMarkerProvider implements LineMarkerProvider { +public class MyCorrectLineMarkerProvider implements LineMarkerProvider { public LineMarkerInfo getLineMarkerInfo(@NotNull PsiElement element) { if (element instanceof PsiIdentifier && element.getParent() instanceof PsiMethod) return new LineMarkerInfo(element, ...); return null; @@ -60,14 +61,17 @@ public class MyLineMarkerProvider implements LineMarkerProvider { } ``` -### 8.2. Register the line marker provider - +## 8.3. Register the Line Marker Provider +The `SimpleLineMarkerProvider` implementation is registered with the IntelliJ Platform using the `codeInsight.lineMarkerProvider` extension point. ```xml - + + + ``` -### 8.3. Run the project - -Now you see the icon on the gutter and can navigate to the property definition. +## 8.4. Run the Project +Run the `simple_language` plugin in a Development Instance and open the [Test file](/tutorials/custom_language_support/annotator.md#run-the-project). +Now the icon appears next to line 3 on the gutter. +A user can click on the icon to navigate to the property definition. ![Line Marker](img/line_marker.png) diff --git a/tutorials/custom_language_support/psi_helper_and_utilities.md b/tutorials/custom_language_support/psi_helper_and_utilities.md index 61b6fe528..bae3c773c 100644 --- a/tutorials/custom_language_support/psi_helper_and_utilities.md +++ b/tutorials/custom_language_support/psi_helper_and_utilities.md @@ -2,24 +2,19 @@ title: 6. PSI Helpers and Utilities --- +Helper classes and utilities can be embedded in the code generated by Grammar-Kit. -### 6.1. Define helper methods for generated PSI elements - -If we want to have custom methods in PSI classes we need to define them separately and ask Grammar-Kit to embed them into generated code. - -Let's define a utility class with these helper methods. +* bullet item +{:toc} +## 6.1. Define Helper Methods for Generated PSI Elements +Custom methods in PSI classes are defined separately, and Grammar-Kit embeds them into generated code. +Define a utility class with these helper methods: ```java -package com.simpleplugin.psi.impl; +package com.intellij.sdk.language.psi.impl; import com.intellij.lang.ASTNode; -import com.intellij.navigation.ItemPresentation; -import com.intellij.psi.*; -import com.simpleplugin.SimpleIcons; -import com.simpleplugin.psi.*; -import org.jetbrains.annotations.Nullable; - -import javax.swing.*; +import com.intellij.sdk.language.psi.*; public class SimplePsiImplUtil { public static String getKey(SimpleProperty element) { @@ -31,7 +26,7 @@ public class SimplePsiImplUtil { return null; } } - + public static String getValue(SimpleProperty element) { ASTNode valueNode = element.getNode().findChildByType(SimpleTypes.VALUE); if (valueNode != null) { @@ -43,13 +38,12 @@ public class SimplePsiImplUtil { } ``` -Note that the `SimpleProperty` interface referenced in the code above is generated by the parser. Also note that the `element.getKey` method used in `getPresentableText` will not be defined - it's a method generated by the parser, which we'll fix below. +The parser generates the `SimpleProperty` interface referenced in the code above. -### 6.2. Update grammar and regenerate the parser - -Now we tell to use this utility class in the grammar file via `psiImplUtilClass` attribute. - -To tell which methods for which PSI classes must be used we specify methods for particular rule. +## 6.2. Update Grammar and Regenerate the Parser +Now the utility class is added to the grammar file via the `psiImplUtilClass` attribute. +Add methods for a particular rule to specify which one should be used for PSI classes. +Compare the last line of the grammar below to the [previous definition](/tutorials/custom_language_support/grammar_and_parser.md#33-define-the-grammar). ```java { @@ -76,13 +70,12 @@ private item_ ::= (property|COMMENT|CRLF) property ::= (KEY? SEPARATOR VALUE?) | KEY {methods=[getKey getValue]} ``` -After we made our changes to the grammar we can regenerate the parser and PSI classes. - -### 6.3. Define a utility to search properties - -Now we need a utility class to search PSI elements for defined properties over the project. -We will use this utility later when implementing code assistance. +After making changes to the grammar, regenerate the parser and PSI classes. +## 6.3. Define a Utility to Search Properties +Create a utility class to search PSI elements for defined properties over the project. +This utility will be used later when implementing [code completion](https://www.jetbrains.com/help/idea/auto-completing-code.html). ```java -{% include /code_samples/simple_language_plugin/src/com/simpleplugin/SimpleUtil.java %} +{% include /code_samples/simple_language/src/main/java/com/intellij/sdk/language/SimpleUtil.java %} ``` + diff --git a/tutorials/custom_language_support/quick_fix.md b/tutorials/custom_language_support/quick_fix.md index 813571ba8..4e41d9fb7 100644 --- a/tutorials/custom_language_support/quick_fix.md +++ b/tutorials/custom_language_support/quick_fix.md @@ -2,35 +2,47 @@ title: 18. Quick Fix --- +A quick fix for a custom language supports the IntelliJ Platform-based IDE feature [Intention Actions](https://www.jetbrains.com/help/idea/intention-actions.html#apply-intention-actions). +For the Simple language, this tutorial adds a quick fix that helps to define an unresolved property from its usage. -A quick fix allows to apply an automatic changes to the code via **Show Intention Actions** (Alt + Enter). - -Let's add a quick fix which helps to define an unresolved property from its usage. - -### 18.1. Update the element factory +* bullet list +{:toc} +## 18.1. Update the Element Factory +The `SimpleElementFactory` is updated to include two new methods to support the user choice of creating a new property for the Simple language quick fix. +The new `createCRLF()` method supports adding a newline to the end of the [`test.simple`](/tutorials/custom_language_support/lexer_and_parser_definition.md#run-the-project) file before adding a new property. +A new overload of `createProperty()` creates a new `key`-`value` pair for Simple language. ```java -{% include /code_samples/simple_language_plugin/src/com/simpleplugin/psi/SimpleElementFactory.java %} +{% include /code_samples/simple_language/src/main/java/com/intellij/sdk/language/psi/SimpleElementFactory.java %} ``` -### 18.2. Define an intention action - -The quick fix will create a property in the file chosen by user, and navigate to this property after creation. - +## 18.2. Define an Intention Action +The `SimpleCreatePropertyQuickFix` will create a property in the file chosen by the user - in this case a Java file containing a `prefix:key` - and navigate to this property after creation. +Under the hood, `SimpleCreatePropertyQuickFix` is an intention action. +For a more in-depth example of an intention action, see [`conditional_operator_intention`](/code_samples/conditional_operator_intention). ```java -{% include /code_samples/simple_language_plugin/src/com/simpleplugin/CreatePropertyQuickFix.java %} +{% include /code_samples/simple_language/src/main/java/com/intellij/sdk/language/SimpleCreatePropertyQuickFix.java %} ``` -### 18.3. Update the annotator - -Note the call to `registerFix`. - +## 18.3. Update the Annotator +When a `badProperty` annotation is created, the `badProperty.registerFix()` method is called. +This method call registers the `SimpleCreatePropertyQuickFix` as the intention action for the Intellij Platform to use to correct the problem. ```java -{% include /code_samples/simple_language_plugin/src/com/simpleplugin/SimpleAnnotator.java %} +{% include /code_samples/simple_language/src/main/java/com/intellij/sdk/language/SimpleAnnotator.java %} ``` -### 18.4. Run the project +## 18.4. Run the project +Open the test [Java file](/tutorials/custom_language_support/annotator.md#run-the-project) in an IDE Development Instance running the `simple_language` plugin. -Now let's try to use a property which is not defined yet. +To test `SimpleCreatePropertyQuickFix`, change `simple:website` to `simple:website.url`. +The key `website.url` is highlighted by `SimpleAnnotator` as an invalid key, as shown below. +Choose "Create Property". -![Quick Fix](img/quick_fix.png) +![Quick Fix](img/quick_fix.png){:width="800px"} + +The IDE will open the `test.simple` file and add `website.url` as a new key. +Add the new value `jetbrains.com` for the new `website.url` key. + +![New Property](img/new_property.png){:width="800px"} + +Now switch back to the Java file; the new key is highlighted as valid. \ No newline at end of file diff --git a/tutorials/custom_language_support/reference_contributor.md b/tutorials/custom_language_support/reference_contributor.md index 6fa2042d1..b997afdca 100644 --- a/tutorials/custom_language_support/reference_contributor.md +++ b/tutorials/custom_language_support/reference_contributor.md @@ -2,25 +2,24 @@ title: 10. Reference Contributor --- - -References is one of the most important and tricky parts in the implementation of a custom language support. +The [References functionality](/reference_guide/custom_language_support/references_and_resolve.md) is one of the most important parts in the implementation of custom language support. Resolving references means the ability to go from the usage of an element to its declaration, completion, rename refactoring, find usages, etc. -**Every element which can be renamed or referenced needs to implement `com.intellij.psi.PsiNamedElement` interface.** +> **Note** Every PSI element that can be renamed or referenced needs to implement [`PsiNamedElement`](upsource:///platform/core-api/src/com/intellij/psi/PsiNamedElement.java) interface. -### 10.1. Define a base named element class +* bullet list +{:toc} -```java -{% include /code_samples/simple_language_plugin/src/com/simpleplugin/psi/SimpleNamedElement.java %} -``` +### 10.1. Define a Named Element Class +The simplified class diagram below shows how the Simple language fulfills the need to implement `PsiNamedElement`. +The `SimpleNamedElement` interface is subclassed from [`PsiNameIdentifierOwner`](). +The `SimpleNamedElementImpl` class implements the `SimpleNamedElement` interface and extends [`ASTWrapperPsiElement`](upsource:///platform/core-impl/src/com/intellij/extapi/psi/ASTWrapperPsiElement.java). -```java -{% include /code_samples/simple_language_plugin/src/com/simpleplugin/psi/impl/SimpleNamedElementImpl.java %} -``` - -### 10.2. Define helper methods for generated PSI elements - -Since we need to implement new methods in PSI class, we should define them in the `SimplePsiImplUtil` class: +![SimpleNamedElementImpl class hierarchy](img/simple_named_element.png){:width="400px"} + +## 10.2. Define Helper Methods for Generated PSI Elements +Modify `SimplePsiImplUtil` to support new methods that will get added to the PSI class for Simple language. +Note that `SimpleElementFactory` isn't defined until the [next step](#103-define-an-element-factory), so for now it shows as an error. ```java public class SimplePsiImplUtil { @@ -55,89 +54,71 @@ public class SimplePsiImplUtil { } ``` -Note that the `SimpleElementFactory` class will show as an error. We'll create it next. - -### 10.3. Define an element factory - +## 10.3. Define an Element Factory +The `SimpleElementFactory` provides methods for creating `SimpleFile`. ```java -package com.simpleplugin.psi; - -import com.intellij.openapi.project.Project; -import com.intellij.psi.*; -import com.simpleplugin.SimpleFileType; - -public class SimpleElementFactory { - public static SimpleProperty createProperty(Project project, String name) { - final SimpleFile file = createFile(project, name); - return (SimpleProperty) file.getFirstChild(); - } - - public static SimpleFile createFile(Project project, String text) { - String name = "dummy.simple"; - return (SimpleFile) PsiFileFactory.getInstance(project). - createFileFromText(name, SimpleFileType.INSTANCE, text); - } -} +{% include /code_samples/simple_language/src/main/java/com/intellij/sdk/language/psi/SimpleElementFactory.java %} ``` -### 10.4. Update grammar and regenerate the parser - -Now we need to make corresponding changes to the grammar file and regenerate parser and PSI classes. - +## 10.4. Update Grammar and Regenerate the Parser +Now make corresponding changes to the `Simple.bnf` grammar file by replacing the `property` definition with the line below. +Don't forget to regenerate the parser after updating the file! +Right click on the `Simple.bnf` file and select **Generate Parser Code**. ```java -property ::= (KEY? SEPARATOR VALUE?) | KEY {mixin="com.simpleplugin.psi.impl.SimpleNamedElementImpl" - implements="com.simpleplugin.psi.SimpleNamedElement" methods=[getKey getValue getName setName getNameIdentifier]} +property ::= (KEY? SEPARATOR VALUE?) | KEY {mixin="com.intellij.sdk.language.psi.impl.SimpleNamedElementImpl" + implements="com.intellij.sdk.language.psi.SimpleNamedElement" methods=[getKey getValue getName setName getNameIdentifier]} ``` -Don't forget to regenerate the parser! Right click on the `Simple.bnf` file and select _Generate Parser Code_. - -### 10.5. Define a reference - -Now we need to define a reference class to resolve a property from it's usage. - +## 10.5. Define a Reference +Now define a reference class to resolve a property from its usage. ```java -{% include /code_samples/simple_language_plugin/src/com/simpleplugin/SimpleReference.java %} +{% include /code_samples/simple_language/src/main/java/com/intellij/sdk/language/SimpleReference.java %} ``` -### 10.6. Define a reference contributor - -A reference contributor allows you to provide references from elements in other languages such as Java to elements in your language. -Let's contribute a reference to each usage of a property. - +## 10.6. Define a Reference Contributor +A reference contributor allows the `simple_language` plugin to provide references to Simple language from elements in other languages such as Java. +Create `SimpleReferenceContributor` by subclassing [`PsiReferenceContributor`](upsource:///platform/core-api/src/com/intellij/psi/PsiReferenceContributor.java). +Contribute a reference to each usage of a property: ```java -{% include /code_samples/simple_language_plugin/src/com/simpleplugin/SimpleReferenceContributor.java %} +{% include /code_samples/simple_language/src/main/java/com/intellij/sdk/language/SimpleReferenceContributor.java %} ``` -### 10.7. Register the reference contributor - +## 10.7. Register the Reference Contributor +The `SimpleReferenceContributor` implementation is registered with the IntelliJ Platform using the `psi.referenceContributor` extension point. ```xml - + + + ``` -### 10.8. Run the project +## 10.8. Run the Project +Rebuild the project, and run `simple_language` in a Development Instance. +The IDE now resolves the property and provides [completion](https://www.jetbrains.com/help/idea/auto-completing-code.html#basic_completion) suggestions: -As you see the IDE now resolves the property and provides completion. +![Reference Contributor](img/reference_contributor.png){:width="800px"} -![Reference Contributor](img/reference_contributor.png) +The [Rename refactoring](https://www.jetbrains.com/help/idea/rename-refactorings.html#invoke-rename-refactoring) functionality is now available from definition and usages. -*Rename* refactoring available from definition and usages. - -![Rename](img/rename.png) - -### 10.9. Define a refactoring support provider - -To allow in-place refactoring we should specify it explicitly in a refactoring support provider. +![Rename](img/rename.png){:width="800px"} +## 10.9. Define a Refactoring Support Provider +To allow in-place refactoring it is specified explicitly in a refactoring support provider. +Create `SimpleRefactoringSupportProvider` by subclassing [`RefactoringSupportProvider`](upsource:///platform/lang-api/src/com/intellij/lang/refactoring/RefactoringSupportProvider.java) +As long as an element is a `SimpleProperty` it is allowed to be refactored: ```java -{% include /code_samples/simple_language_plugin/src/com/simpleplugin/SimpleRefactoringSupportProvider.java %} +{% include /code_samples/simple_language/src/main/java/com/intellij/sdk/language/SimpleRefactoringSupportProvider.java %} ``` -### 10.10. Register the refactoring support provider - +## 10.10. Register the Refactoring Support Provider +The `SimpleRefactoringSupportProvider` implementation is registered with the IntelliJ Platform using the `lang.refactoringSupport` extension point. ```xml - + + + ``` -### 10.11. Run the project +## 10.11. Run the Project +Rebuild the project, and run `simple_language` in a Development Instance. +The IDE now supports [refactoring](https://www.jetbrains.com/help/idea/rename-refactorings.html) suggestions: -![In Place Rename](img/in_place_rename.png) +![In Place Rename](img/in_place_rename.png){:width="800px"} diff --git a/tutorials/custom_language_support/structure_view_factory.md b/tutorials/custom_language_support/structure_view_factory.md index 414fa4966..5136f1a24 100644 --- a/tutorials/custom_language_support/structure_view_factory.md +++ b/tutorials/custom_language_support/structure_view_factory.md @@ -2,33 +2,46 @@ title: 14. Structure View Factory --- +The [structure view](/reference_guide/custom_language_support/structure_view.md) in the IntelliJ Platform-based IDE can be customized for a specific file type, including Simple language. +Creating a structure view factory allows showing the structure of any file in a **Structure** tool window for easy navigation between items. -A structure view factory allows to show the structure of any file in a *Structure* tool window for easy navigation between items. - -### 14.1. Define a structure view factory +* bullet list +{:toc} +## 14.1. Define a Structure View Factory +The structure view factory implements [`PsiStructureViewFactory`](upsource:///community/platform/editor-ui-api/src/com/intellij/lang/PsiStructureViewFactory.java). +The `getStructureViewBuilder()` implementation reuses the IntelliJ Platform class [`TreeBasedStructureViewBuilder`](upsource:///community/platform/editor-ui-api/src/com/intellij/ide/structureView/TreeBasedStructureViewBuilder.java). +At this point the project will not compile until `SimpleStructureViewModel` is implemented below. ```java -{% include /code_samples/simple_language_plugin/src/com/simpleplugin/SimpleStructureViewFactory.java %} +{% include /code_samples/simple_language/src/main/java/com/intellij/sdk/language/SimpleStructureViewFactory.java %} ``` -### 14.2. Define a structure view model - +## 14.2. Define a Structure View Model +The `SimpleStructureViewModel` is created by implementing [`StructureViewModel`](upsource:///platform/editor-ui-api/src/com/intellij/ide/structureView/StructureViewModel.java), which defines the model for data displayed in the standard structure view. +It also extends [`StructureViewModelBase`](upsource:///community/platform/editor-ui-api/src/com/intellij/ide/structureView/StructureViewModelBase.java), an implementation that links the model to a text editor. ```java -{% include /code_samples/simple_language_plugin/src/com/simpleplugin/SimpleStructureViewModel.java %} +{% include /code_samples/simple_language/src/main/java/com/intellij/sdk/language/SimpleStructureViewModel.java %} ``` -### 14.3. Define a structure view element - +## 14.3. Define a Structure View Element +The `SimpleStructureViewElement` implements [`StructureViewTreeElement`](upsource:///platform/editor-ui-api/src/com/intellij/ide/structureView/StructureViewTreeElement.java) and [`SortableTreeElement`](upsource:///platform/editor-ui-api/src/com/intellij/ide/util/treeView/smartTree/SortableTreeElement.java). +The `StructureViewTreeElement` represents an element in the Structure View tree model. +The `SortableTreeElement` represents an item in a smart tree that allows using text other than the presentable text as a key for alphabetic sorting. ```java -{% include /code_samples/simple_language_plugin/src/com/simpleplugin/SimpleStructureViewElement.java %} +{% include /code_samples/simple_language/src/main/java/com/intellij/sdk/language/SimpleStructureViewElement.java %} ``` -### 14.4. Register the structure view factory - +## 14.4. Register the Structure View Factory +The `SimpleStructureViewFactory` implementation is registered with the IntelliJ Platform in `plugin.xml` using the `lang.psiStructureViewFactory` extension point. ```xml - + + + ``` -### 14.5. Run the project +## 14.5. Run the Project +Rebuild the project, and run `simple_language` in a Development Instance. +Open the `test.simple` file and choose **View \| Tool Windows \| Structure**. +The IDE now supports a structure view of the Simple language: -![Structure View](img/structure_view.png) +![Structure View](img/structure_view.png){:width="800px"} diff --git a/tutorials/custom_language_support/syntax_highlighter_and_color_settings_page.md b/tutorials/custom_language_support/syntax_highlighter_and_color_settings_page.md index eefc0f998..134fa56f3 100644 --- a/tutorials/custom_language_support/syntax_highlighter_and_color_settings_page.md +++ b/tutorials/custom_language_support/syntax_highlighter_and_color_settings_page.md @@ -2,41 +2,58 @@ title: 5. Syntax Highlighter and Color Settings Page --- +The first level of [syntax highlighting](/reference_guide/custom_language_support/syntax_highlighting_and_error_highlighting.md#lexer) is based on the lexer output, and is provided by `SyntaxHighlighter`. +A plugin can also define [color settings](/reference_guide/custom_language_support/syntax_highlighting_and_error_highlighting.md#color-settings) based on `ColorSettingPage` so the user can configure highlight colors. +The `SimpleSyntaxHighlighter`, `SimpleSyntaxHighlighterFactory`, and `SimpleColorSettingsPage` discussed on this page are demonstrated in the `simple_language` code sample. -### 5.1. Define a syntax highlighter +* bullet list +{:toc} +## 5.1. Define a Syntax Highlighter +The Simple language syntax highlighter class extends [`SyntaxHighlighterBase`](upsource:///platform/editor-ui-api/src/com/intellij/openapi/fileTypes/SyntaxHighlighterBase.java). +As recommended in [Color Scheme Management](/reference_guide/color_scheme_management.md#text-attribute-key-dependency), the Simple language highlighting text attributes are specified as a dependency on one of standard Intellij Platform keys. +For the Simple language, only one scheme is defined. ```java -{% include /code_samples/simple_language_plugin/src/com/simpleplugin/SimpleSyntaxHighlighter.java %} +{% include /code_samples/simple_language/src/main/java/com/intellij/sdk/language/SimpleSyntaxHighlighter.java %} ``` -### 5.2. Define a syntax highlighter factory - +### 5.2. Define a Syntax Highlighter Factory +The factory provides a standard way for the IntelliJ Platform to instantiate the syntax highlighter for Simple language files. ```java -{% include /code_samples/simple_language_plugin/src/com/simpleplugin/SimpleSyntaxHighlighterFactory.java %} +{% include /code_samples/simple_language/src/main/java/com/intellij/sdk/language/SimpleSyntaxHighlighterFactory.java %} ``` -### 5.3. Register the syntax highlighter factory - +### 5.3. Register the Syntax Highlighter Factory +The factory is registered with the IntelliJ Platform using the plugin configuration file. ```xml - + + + ``` -### 5.4. Run the project +### 5.4. Run the Project +Open the example Simple Language [properties file ](/tutorials/custom_language_support/lexer_and_parser_definition.md#47-run-the-project) in the IDE Development Instance. +The colors for Simple Language Key, Separator, and Value highlighting default to the IDE _Language Defaults_ for Keyword, Braces and Operators, and String, respectively. -![Syntax highlighter](img/syntax_highlighter.png) - -### 5.5. Define a color settings page +![Syntax highlighter](img/syntax_highlighter.png){:width="800px"} +## 5.5. Define a Color Settings Page +The color settings page adds the ability for users to customize color settings for the highlighting in Simple language files. +The `SimpleColorSettingsPage` extends `ColorSettingsPage`. ```java -{% include /code_samples/simple_language_plugin/src/com/simpleplugin/SimpleColorSettingsPage.java %} +{% include /code_samples/simple_language/src/main/java/com/intellij/sdk/language/SimpleColorSettingsPage.java %} ``` -### 5.6. Register the color settings page - +### 5.6. Register the Color Settings Page +The settings page for Simple language colors is registered in the plugin configuration file as an extension. ```xml - + + + ``` ### 5.7. Run the project +In the IDE Development Instance, open the Simple language highlight settings page: **Preferences/Settings \| Editor \| Color Scheme \| Simple**. +Each color initially inherits from a _Language Defaults_ value. -![Color Settings Page](img/color_settings_page.png) +![Color Settings Page](img/color_settings_page.png){:width="800px"} diff --git a/tutorials/custom_language_support_tutorial.md b/tutorials/custom_language_support_tutorial.md index 54b517d0c..178c395d5 100644 --- a/tutorials/custom_language_support_tutorial.md +++ b/tutorials/custom_language_support_tutorial.md @@ -2,14 +2,11 @@ title: Custom Language Support Tutorial --- -In this tutorial we will add support for -[.properties](https://en.wikipedia.org/wiki/.properties) -language and its usages within Java code. - -The final code can be found in the [SamplePlugin repo on GitHub](https://github.com/JetBrains/intellij-sdk-docs/tree/master/code_samples/simple_language_plugin). - -This a step-by-step tutorial and it requires performing every step: - +In this tutorial we will add support for a [.properties](https://en.wikipedia.org/wiki/.properties) language and its usages within Java code. +IntelliJ Platform support for custom languages is discussed in more depth in the [Custom Language](/reference_guide/custom_language_support.md) section. + +The example plugin used in this tutorial is the `simple_language` code sample. +This a step-by-step tutorial, and it requires completing each step, in order: * [1. Prerequisites](custom_language_support/prerequisites.md) * [2. Language and File Type](custom_language_support/language_and_filetype.md) * [3. Grammar and Parser](custom_language_support/grammar_and_parser.md)