mirror of
https://github.com/JetBrains/intellij-sdk-code-samples.git
synced 2025-07-29 17:57:53 +08:00
Reference code-samples directly (as per DRY)
This commit is contained in:
parent
80a4c16b4a
commit
b940673f47
22
Gemfile.lock
22
Gemfile.lock
@ -62,13 +62,33 @@ GEM
|
|||||||
mini_portile2 (~> 2.0.0.rc2)
|
mini_portile2 (~> 2.0.0.rc2)
|
||||||
nokogiri (1.6.7.1-x64-mingw32)
|
nokogiri (1.6.7.1-x64-mingw32)
|
||||||
mini_portile2 (~> 2.0.0.rc2)
|
mini_portile2 (~> 2.0.0.rc2)
|
||||||
|
octopress (3.0.11)
|
||||||
|
jekyll (>= 2.0)
|
||||||
|
mercenary (~> 0.3.2)
|
||||||
|
octopress-deploy
|
||||||
|
octopress-escape-code (~> 2.0)
|
||||||
|
octopress-hooks (~> 2.0)
|
||||||
|
redcarpet (~> 3.0)
|
||||||
|
titlecase
|
||||||
|
octopress-code-highlighter (4.3.0)
|
||||||
|
jekyll (~> 3.0)
|
||||||
|
octopress-deploy (1.3.0)
|
||||||
|
colorator
|
||||||
|
octopress-escape-code (2.1.1)
|
||||||
|
jekyll (~> 3.0)
|
||||||
|
octopress-hooks (2.6.1)
|
||||||
|
jekyll (>= 2.0)
|
||||||
|
octopress-render-code (1.0.4)
|
||||||
|
octopress-code-highlighter (~> 4.2)
|
||||||
rake (10.4.2)
|
rake (10.4.2)
|
||||||
rb-fsevent (0.9.6)
|
rb-fsevent (0.9.6)
|
||||||
rb-inotify (0.9.5)
|
rb-inotify (0.9.5)
|
||||||
ffi (>= 0.5.0)
|
ffi (>= 0.5.0)
|
||||||
|
redcarpet (3.3.3)
|
||||||
robotex (1.0.0)
|
robotex (1.0.0)
|
||||||
safe_yaml (1.0.4)
|
safe_yaml (1.0.4)
|
||||||
sass (3.4.20)
|
sass (3.4.20)
|
||||||
|
titlecase (0.1.1)
|
||||||
trollop (2.1.2)
|
trollop (2.1.2)
|
||||||
|
|
||||||
PLATFORMS
|
PLATFORMS
|
||||||
@ -80,6 +100,8 @@ DEPENDENCIES
|
|||||||
jekyll-git_metadata!
|
jekyll-git_metadata!
|
||||||
jekyll-redirect-from
|
jekyll-redirect-from
|
||||||
link-checker!
|
link-checker!
|
||||||
|
octopress (~> 3.0)
|
||||||
|
octopress-render-code
|
||||||
rake
|
rake
|
||||||
rouge!
|
rouge!
|
||||||
|
|
||||||
|
@ -78,68 +78,9 @@ Create the following directory structure:
|
|||||||
|
|
||||||

|

|
||||||
|
|
||||||
```java
|
{% include_code gradle_plugin_demo/src/main/java/HelloAction.java %}
|
||||||
import com.intellij.openapi.actionSystem.AnAction;
|
|
||||||
import com.intellij.openapi.actionSystem.AnActionEvent;
|
|
||||||
import com.intellij.openapi.actionSystem.PlatformDataKeys;
|
|
||||||
import com.intellij.openapi.project.Project;
|
|
||||||
import com.intellij.openapi.ui.Messages;
|
|
||||||
|
|
||||||
public class HelloAction extends AnAction {
|
{% include_code gradle_plugin_demo/src/main/resources/META-INF/plugin.xml %}
|
||||||
public HelloAction() {
|
|
||||||
super("Hello");
|
|
||||||
}
|
|
||||||
|
|
||||||
public void actionPerformed(AnActionEvent event) {
|
|
||||||
Project project = event.getData(PlatformDataKeys.PROJECT);
|
|
||||||
Messages.showMessageDialog(project, "Hello world!", "Greeting", Messages.getInformationIcon());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
```xml
|
|
||||||
<idea-plugin version="2">
|
|
||||||
<id>org.jetbrains</id>
|
|
||||||
<name>gradle_plugin_demo</name>
|
|
||||||
<version>0.0.1</version>
|
|
||||||
<vendor email="dummy" url="dummy">dummy</vendor>
|
|
||||||
|
|
||||||
<description><![CDATA[
|
|
||||||
Sample plugin.<br>
|
|
||||||
]]></description>
|
|
||||||
|
|
||||||
<change-notes><![CDATA[
|
|
||||||
Release 0.0.1: Initial release.<br>
|
|
||||||
]]>
|
|
||||||
</change-notes>
|
|
||||||
|
|
||||||
<!-- please see http://www.jetbrains.org/intellij/sdk/docs/basics/getting_started/build_number_ranges.html for description -->
|
|
||||||
<idea-version since-build="131"/>
|
|
||||||
|
|
||||||
<!-- please see https://confluence.jetbrains.com/display/IDEADEV/Plugin+Compatibility+with+IntelliJ+Platform+Products
|
|
||||||
on how to target different products -->
|
|
||||||
<!-- uncomment to enable plugin in all products
|
|
||||||
<depends>com.intellij.modules.lang</depends>
|
|
||||||
-->
|
|
||||||
|
|
||||||
<extensions defaultExtensionNs="com.intellij">
|
|
||||||
</extensions>
|
|
||||||
|
|
||||||
<application-components>
|
|
||||||
</application-components>
|
|
||||||
|
|
||||||
<project-components>
|
|
||||||
</project-components>
|
|
||||||
|
|
||||||
<actions>
|
|
||||||
<group id="MyPlugin.SampleMenu" text="Greeting" description="Greeting menu">
|
|
||||||
<add-to-group group-id="MainMenu" anchor="last" />
|
|
||||||
<action id="Myplugin.Textboxes" class="HelloAction" text="Hello" description="Says hello" />
|
|
||||||
</group>
|
|
||||||
</actions>
|
|
||||||
|
|
||||||
</idea-plugin>
|
|
||||||
```
|
|
||||||
|
|
||||||
Add a new Gradle Run Configuration, configured like so:
|
Add a new Gradle Run Configuration, configured like so:
|
||||||
|
|
||||||
|
@ -4,71 +4,11 @@ title: 16. Code Style Setting
|
|||||||
|
|
||||||
### 16.1. Define code style settings
|
### 16.1. Define code style settings
|
||||||
|
|
||||||
```java
|
{% include_code simple_language_plugin/src/com/simpleplugin/SimpleCodeStyleSettings.java %}
|
||||||
package com.simpleplugin;
|
|
||||||
|
|
||||||
import com.intellij.psi.codeStyle.CodeStyleSettings;
|
|
||||||
import com.intellij.psi.codeStyle.CustomCodeStyleSettings;
|
|
||||||
|
|
||||||
public class SimpleCodeStyleSettings extends CustomCodeStyleSettings {
|
|
||||||
public SimpleCodeStyleSettings(CodeStyleSettings settings) {
|
|
||||||
super("SimpleCodeStyleSettings", settings);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 16.2. Define code style settings provider
|
### 16.2. Define code style settings provider
|
||||||
|
|
||||||
```java
|
{% include_code simple_language_plugin/src/com/simpleplugin/SimpleCodeStyleSettingsProvider.java %}
|
||||||
package com.simpleplugin;
|
|
||||||
|
|
||||||
import com.intellij.application.options.CodeStyleAbstractConfigurable;
|
|
||||||
import com.intellij.application.options.CodeStyleAbstractPanel;
|
|
||||||
import com.intellij.application.options.TabbedLanguageCodeStylePanel;
|
|
||||||
import com.intellij.openapi.options.Configurable;
|
|
||||||
import com.intellij.psi.codeStyle.CodeStyleSettings;
|
|
||||||
import com.intellij.psi.codeStyle.CodeStyleSettingsProvider;
|
|
||||||
import com.intellij.psi.codeStyle.CustomCodeStyleSettings;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
|
|
||||||
public class SimpleCodeStyleSettingsProvider extends CodeStyleSettingsProvider {
|
|
||||||
@Override
|
|
||||||
public CustomCodeStyleSettings createCustomSettings(CodeStyleSettings settings) {
|
|
||||||
return new SimpleCodeStyleSettings(settings);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
@Override
|
|
||||||
public String getConfigurableDisplayName() {
|
|
||||||
return "Simple";
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
@Override
|
|
||||||
public Configurable createSettingsPage(CodeStyleSettings settings, CodeStyleSettings originalSettings) {
|
|
||||||
return new CodeStyleAbstractConfigurable(settings, originalSettings, "Simple") {
|
|
||||||
@Override
|
|
||||||
protected CodeStyleAbstractPanel createPanel(CodeStyleSettings settings) {
|
|
||||||
return new SimpleCodeStyleMainPanel(getCurrentSettings(), settings);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
@Override
|
|
||||||
public String getHelpTopic() {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class SimpleCodeStyleMainPanel extends TabbedLanguageCodeStylePanel {
|
|
||||||
public SimpleCodeStyleMainPanel(CodeStyleSettings currentSettings, CodeStyleSettings settings) {
|
|
||||||
super(SimpleLanguage.INSTANCE, currentSettings, settings);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
### 16.3. Register the code style settings provider
|
### 16.3. Register the code style settings provider
|
||||||
|
|
||||||
@ -78,50 +18,7 @@ public class SimpleCodeStyleSettingsProvider extends CodeStyleSettingsProvider {
|
|||||||
|
|
||||||
### 16.4. Define language code style settings provider
|
### 16.4. Define language code style settings provider
|
||||||
|
|
||||||
```java
|
{% include_code simple_language_plugin/src/com/simpleplugin/SimpleLanguageCodeStyleSettingsProvider.java %}
|
||||||
package com.simpleplugin;
|
|
||||||
|
|
||||||
import com.intellij.lang.Language;
|
|
||||||
import com.intellij.psi.codeStyle.CodeStyleSettingsCustomizable;
|
|
||||||
import com.intellij.psi.codeStyle.LanguageCodeStyleSettingsProvider;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
|
|
||||||
public class SimpleLanguageCodeStyleSettingsProvider extends LanguageCodeStyleSettingsProvider {
|
|
||||||
@NotNull
|
|
||||||
@Override
|
|
||||||
public Language getLanguage() {
|
|
||||||
return SimpleLanguage.INSTANCE;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void customizeSettings(@NotNull CodeStyleSettingsCustomizable consumer, @NotNull SettingsType settingsType) {
|
|
||||||
if (settingsType == SettingsType.SPACING_SETTINGS) {
|
|
||||||
consumer.showStandardOptions("SPACE_AROUND_ASSIGNMENT_OPERATORS");
|
|
||||||
consumer.renameStandardOption("SPACE_AROUND_ASSIGNMENT_OPERATORS", "Separator");
|
|
||||||
} else if (settingsType == SettingsType.BLANK_LINES_SETTINGS) {
|
|
||||||
consumer.showStandardOptions("KEEP_BLANK_LINES_IN_CODE");
|
|
||||||
} else if (settingsType == SettingsType.WRAPPING_AND_BRACES_SETTINGS) {
|
|
||||||
consumer.showStandardOptions("KEEP_LINE_BREAKS");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getCodeSample(@NotNull SettingsType settingsType) {
|
|
||||||
return "# You are reading the \".properties\" entry.\n" +
|
|
||||||
"! The exclamation mark can also mark text as comments.\n" +
|
|
||||||
"website = http://en.wikipedia.org/\n" +
|
|
||||||
"language = English\n" +
|
|
||||||
"# The backslash below tells the application to continue reading\n" +
|
|
||||||
"# the value onto the next line.\n" +
|
|
||||||
"message = Welcome to \\\n" +
|
|
||||||
" Wikipedia!\n" +
|
|
||||||
"# Add spaces to the key\n" +
|
|
||||||
"key\\ with\\ spaces = This is the value that could be looked up with the key \"key with spaces\".\n" +
|
|
||||||
"# Unicode\n" +
|
|
||||||
"tab : \\u0009";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 16.5. Register the language code style settings provider
|
### 16.5. Register the language code style settings provider
|
||||||
|
|
||||||
|
@ -6,44 +6,7 @@ A commenter allows user to comment the code at the cursor or selected code autom
|
|||||||
|
|
||||||
### 17.1. Define a commenter
|
### 17.1. Define a commenter
|
||||||
|
|
||||||
```java
|
{% include_code simple_language_plugin/src/com/simpleplugin/SimpleCommenter.java %}
|
||||||
package com.simpleplugin;
|
|
||||||
|
|
||||||
import com.intellij.lang.Commenter;
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
|
|
||||||
public class SimpleCommenter implements Commenter {
|
|
||||||
@Nullable
|
|
||||||
@Override
|
|
||||||
public String getLineCommentPrefix() {
|
|
||||||
return "#";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
@Override
|
|
||||||
public String getBlockCommentPrefix() {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
@Override
|
|
||||||
public String getBlockCommentSuffix() {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
@Override
|
|
||||||
public String getCommentedBlockCommentPrefix() {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
@Override
|
|
||||||
public String getCommentedBlockCommentSuffix() {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 17.2. Register the commenter
|
### 17.2. Register the commenter
|
||||||
|
|
||||||
|
@ -9,31 +9,7 @@ The easiest way to provide completion is to use a completion contributor.
|
|||||||
|
|
||||||
Let's provide custom completion for values in property files.
|
Let's provide custom completion for values in property files.
|
||||||
|
|
||||||
```java
|
{% include_code simple_language_plugin/src/com/simpleplugin/SimpleCompletionContributor.java %}
|
||||||
package com.simpleplugin;
|
|
||||||
|
|
||||||
import com.intellij.codeInsight.completion.*;
|
|
||||||
import com.intellij.codeInsight.lookup.LookupElementBuilder;
|
|
||||||
import com.intellij.patterns.PlatformPatterns;
|
|
||||||
import com.intellij.util.ProcessingContext;
|
|
||||||
import com.simpleplugin.psi.SimpleTypes;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
|
|
||||||
public class SimpleCompletionContributor extends CompletionContributor {
|
|
||||||
public SimpleCompletionContributor() {
|
|
||||||
extend(CompletionType.BASIC,
|
|
||||||
PlatformPatterns.psiElement(SimpleTypes.VALUE).withLanguage(SimpleLanguage.INSTANCE),
|
|
||||||
new CompletionProvider<CompletionParameters>() {
|
|
||||||
public void addCompletions(@NotNull CompletionParameters parameters,
|
|
||||||
ProcessingContext context,
|
|
||||||
@NotNull CompletionResultSet resultSet) {
|
|
||||||
resultSet.addElement(LookupElementBuilder.create("Hello"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 9.2. Register the completion contributor
|
### 9.2. Register the completion contributor
|
||||||
|
|
||||||
|
@ -7,71 +7,7 @@ A scanner breaks the text into words, defines the context for each word and pass
|
|||||||
|
|
||||||
### 11.1. Define a find usages provider
|
### 11.1. Define a find usages provider
|
||||||
|
|
||||||
```java
|
{% include_code simple_language_plugin/src/com/simpleplugin/SimpleFindUsagesProvider.java %}
|
||||||
package com.simpleplugin;
|
|
||||||
|
|
||||||
import com.intellij.lang.cacheBuilder.DefaultWordsScanner;
|
|
||||||
import com.intellij.lang.cacheBuilder.WordsScanner;
|
|
||||||
import com.intellij.lang.findUsages.FindUsagesProvider;
|
|
||||||
import com.intellij.psi.PsiElement;
|
|
||||||
import com.intellij.psi.PsiNamedElement;
|
|
||||||
import com.intellij.psi.tree.TokenSet;
|
|
||||||
import com.simpleplugin.psi.SimpleProperty;
|
|
||||||
import com.simpleplugin.psi.SimpleTypes;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
|
|
||||||
public class SimpleFindUsagesProvider implements FindUsagesProvider {
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
@Override
|
|
||||||
public WordsScanner getWordsScanner() {
|
|
||||||
return new DefaultWordsScanner(new SimpleLexerAdapter(),
|
|
||||||
TokenSet.create(SimpleTypes.KEY), TokenSet.create(SimpleTypes.COMMENT), TokenSet.EMPTY);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean canFindUsagesFor(@NotNull PsiElement psiElement) {
|
|
||||||
return psiElement instanceof PsiNamedElement;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
@Override
|
|
||||||
public String getHelpId(@NotNull PsiElement psiElement) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
@Override
|
|
||||||
public String getType(@NotNull PsiElement element) {
|
|
||||||
if (element instanceof SimpleProperty) {
|
|
||||||
return "simple property";
|
|
||||||
} else {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
@Override
|
|
||||||
public String getDescriptiveName(@NotNull PsiElement element) {
|
|
||||||
if (element instanceof SimpleProperty) {
|
|
||||||
return ((SimpleProperty) element).getKey();
|
|
||||||
} else {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
@Override
|
|
||||||
public String getNodeText(@NotNull PsiElement element, boolean useFullName) {
|
|
||||||
if (element instanceof SimpleProperty) {
|
|
||||||
return ((SimpleProperty) element).getKey() + ":" + ((SimpleProperty) element).getValue();
|
|
||||||
} else {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 11.2. Register the find usages provider
|
### 11.2. Register the find usages provider
|
||||||
|
|
||||||
|
@ -8,68 +8,7 @@ A folding builder helps you to fold the code regions and replace it with specifi
|
|||||||
|
|
||||||
Let's replace usages of properties with its values by default.
|
Let's replace usages of properties with its values by default.
|
||||||
|
|
||||||
```java
|
{% include_code simple_language_plugin/src/com/simpleplugin/SimpleFoldingBuilder.java %}
|
||||||
package com.simpleplugin;
|
|
||||||
|
|
||||||
import com.intellij.lang.ASTNode;
|
|
||||||
import com.intellij.lang.folding.FoldingBuilderEx;
|
|
||||||
import com.intellij.lang.folding.FoldingDescriptor;
|
|
||||||
import com.intellij.openapi.editor.Document;
|
|
||||||
import com.intellij.openapi.editor.FoldingGroup;
|
|
||||||
import com.intellij.openapi.project.Project;
|
|
||||||
import com.intellij.openapi.util.TextRange;
|
|
||||||
import com.intellij.psi.PsiElement;
|
|
||||||
import com.intellij.psi.PsiLiteralExpression;
|
|
||||||
import com.intellij.psi.util.PsiTreeUtil;
|
|
||||||
import com.simpleplugin.psi.SimpleProperty;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class SimpleFoldingBuilder extends FoldingBuilderEx {
|
|
||||||
@NotNull
|
|
||||||
@Override
|
|
||||||
public FoldingDescriptor[] buildFoldRegions(@NotNull PsiElement root, @NotNull Document document, boolean quick) {
|
|
||||||
FoldingGroup group = FoldingGroup.newGroup("simple");
|
|
||||||
|
|
||||||
List<FoldingDescriptor> descriptors = new ArrayList<FoldingDescriptor>();
|
|
||||||
Collection<PsiLiteralExpression> literalExpressions = PsiTreeUtil.findChildrenOfType(root, PsiLiteralExpression.class);
|
|
||||||
for (final PsiLiteralExpression literalExpression : literalExpressions) {
|
|
||||||
String value = (String) literalExpression.getValue();
|
|
||||||
if (value != null && value.startsWith("simple:")) {
|
|
||||||
Project project = literalExpression.getProject();
|
|
||||||
final List<SimpleProperty> properties = SimpleUtil.findProperties(project, value.substring(7));
|
|
||||||
if (properties.size() == 1) {
|
|
||||||
descriptors.add(new FoldingDescriptor(literalExpression.getNode(),
|
|
||||||
new TextRange(literalExpression.getTextRange().getStartOffset() + 1,
|
|
||||||
literalExpression.getTextRange().getEndOffset() - 1), group) {
|
|
||||||
@Nullable
|
|
||||||
@Override
|
|
||||||
public String getPlaceholderText() {
|
|
||||||
return properties.get(0).getValue();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return descriptors.toArray(new FoldingDescriptor[descriptors.size()]);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
@Override
|
|
||||||
public String getPlaceholderText(@NotNull ASTNode node) {
|
|
||||||
return "...";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isCollapsedByDefault(@NotNull ASTNode node) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 12.2. Register the folding builder
|
### 12.2. Register the folding builder
|
||||||
|
|
||||||
|
@ -9,105 +9,13 @@ title: 15. Formatter
|
|||||||
The formatter uses the blocks to receive formatting rules for each PSI element.
|
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.
|
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.
|
||||||
|
|
||||||
```java
|
{% include_code simple_language_plugin/src/com/simpleplugin/SimpleBlock.java %}
|
||||||
package com.simpleplugin;
|
|
||||||
|
|
||||||
import com.intellij.formatting.*;
|
|
||||||
import com.intellij.lang.ASTNode;
|
|
||||||
import com.intellij.psi.TokenType;
|
|
||||||
import com.intellij.psi.formatter.common.AbstractBlock;
|
|
||||||
import com.simpleplugin.psi.SimpleTypes;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class SimpleBlock extends AbstractBlock {
|
|
||||||
private SpacingBuilder spacingBuilder;
|
|
||||||
|
|
||||||
protected SimpleBlock(@NotNull ASTNode node, @Nullable Wrap wrap, @Nullable Alignment alignment,
|
|
||||||
SpacingBuilder spacingBuilder) {
|
|
||||||
super(node, wrap, alignment);
|
|
||||||
this.spacingBuilder = spacingBuilder;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected List<Block> buildChildren() {
|
|
||||||
List<Block> blocks = new ArrayList<Block>();
|
|
||||||
ASTNode child = myNode.getFirstChildNode();
|
|
||||||
ASTNode previousChild = null;
|
|
||||||
while (child != null) {
|
|
||||||
if (child.getElementType() != TokenType.WHITE_SPACE &&
|
|
||||||
(previousChild == null || previousChild.getElementType() != SimpleTypes.CRLF ||
|
|
||||||
child.getElementType() != SimpleTypes.CRLF)) {
|
|
||||||
Block block = new SimpleBlock(child, Wrap.createWrap(WrapType.NONE, false), Alignment.createAlignment(),
|
|
||||||
spacingBuilder);
|
|
||||||
blocks.add(block);
|
|
||||||
}
|
|
||||||
previousChild = child;
|
|
||||||
child = child.getTreeNext();
|
|
||||||
}
|
|
||||||
return blocks;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Indent getIndent() {
|
|
||||||
return Indent.getNoneIndent();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
@Override
|
|
||||||
public Spacing getSpacing(@Nullable Block child1, @NotNull Block child2) {
|
|
||||||
return spacingBuilder.getSpacing(this, child1, child2);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isLeaf() {
|
|
||||||
return myNode.getFirstChildNode() == null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 15.2. Define a formatting model builder
|
### 15.2. Define a formatting model builder
|
||||||
|
|
||||||
Let's define a formatter which removes extra spaces except the single ones around the property separator.
|
Let's define a formatter which removes extra spaces except the single ones around the property separator.
|
||||||
|
|
||||||
```java
|
{% include_code simple_language_plugin/src/com/simpleplugin/SimpleFormattingModelBuilder.java %}
|
||||||
package com.simpleplugin;
|
|
||||||
|
|
||||||
import com.intellij.formatting.*;
|
|
||||||
import com.intellij.lang.ASTNode;
|
|
||||||
import com.intellij.openapi.util.TextRange;
|
|
||||||
import com.intellij.psi.PsiElement;
|
|
||||||
import com.intellij.psi.PsiFile;
|
|
||||||
import com.intellij.psi.codeStyle.CodeStyleSettings;
|
|
||||||
import com.simpleplugin.psi.SimpleTypes;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
|
|
||||||
public class SimpleFormattingModelBuilder implements FormattingModelBuilder {
|
|
||||||
@NotNull
|
|
||||||
@Override
|
|
||||||
public FormattingModel createModel(PsiElement element, CodeStyleSettings settings) {
|
|
||||||
return FormattingModelProvider.createFormattingModelForPsiFile(element.getContainingFile(),
|
|
||||||
new SimpleBlock(element.getNode(), Wrap.createWrap(WrapType.NONE, false),
|
|
||||||
Alignment.createAlignment(), createSpaceBuilder(settings)), settings);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static SpacingBuilder createSpaceBuilder(CodeStyleSettings settings) {
|
|
||||||
return new SpacingBuilder(settings, SimpleLanguage.INSTANCE).
|
|
||||||
around(SimpleTypes.SEPARATOR).spaceIf(settings.SPACE_AROUND_ASSIGNMENT_OPERATORS).
|
|
||||||
before(SimpleTypes.PROPERTY).none();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
@Override
|
|
||||||
public TextRange getRangeAffectingIndent(PsiFile file, int offset, ASTNode elementAtOffset) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 15.3. Register the formatter
|
### 15.3. Register the formatter
|
||||||
|
|
||||||
|
@ -43,41 +43,7 @@ property ::= (KEY? SEPARATOR VALUE?) | KEY {mixin="com.simpleplugin.psi.impl.Sim
|
|||||||
|
|
||||||
### 13.3. Define a go to symbol contributor
|
### 13.3. Define a go to symbol contributor
|
||||||
|
|
||||||
```java
|
{% include_code simple_language_plugin/src/com/simpleplugin/SimpleChooseByNameContributor.java %}
|
||||||
package com.simpleplugin;
|
|
||||||
|
|
||||||
import com.intellij.navigation.ChooseByNameContributor;
|
|
||||||
import com.intellij.navigation.NavigationItem;
|
|
||||||
import com.intellij.openapi.project.Project;
|
|
||||||
import com.simpleplugin.psi.SimpleProperty;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class SimpleChooseByNameContributor implements ChooseByNameContributor {
|
|
||||||
@NotNull
|
|
||||||
@Override
|
|
||||||
public String[] getNames(Project project, boolean includeNonProjectItems) {
|
|
||||||
List<SimpleProperty> properties = SimpleUtil.findProperties(project);
|
|
||||||
List<String> names = new ArrayList<String>(properties.size());
|
|
||||||
for (SimpleProperty property : properties) {
|
|
||||||
if (property.getKey() != null && property.getKey().length() > 0) {
|
|
||||||
names.add(property.getKey());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return names.toArray(new String[names.size()]);
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
@Override
|
|
||||||
public NavigationItem[] getItemsByName(String name, String pattern, Project project, boolean includeNonProjectItems) {
|
|
||||||
// todo include non project items
|
|
||||||
List<SimpleProperty> properties = SimpleUtil.findProperties(project, name);
|
|
||||||
return properties.toArray(new NavigationItem[properties.size()]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 13.4. Register the go to symbol contributor
|
### 13.4. Register the go to symbol contributor
|
||||||
|
|
||||||
|
@ -4,69 +4,17 @@ title: 3. Grammar and Parser
|
|||||||
|
|
||||||
### 3.1. Define a token type
|
### 3.1. Define a token type
|
||||||
|
|
||||||
```java
|
{% include_code simple_language_plugin/src/com/simpleplugin/psi/SimpleTokenType.java %}
|
||||||
package com.simpleplugin.psi;
|
|
||||||
|
|
||||||
import com.intellij.psi.tree.IElementType;
|
|
||||||
import com.simpleplugin.SimpleLanguage;
|
|
||||||
import org.jetbrains.annotations.NonNls;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
|
|
||||||
public class SimpleTokenType extends IElementType {
|
|
||||||
public SimpleTokenType(@NotNull @NonNls String debugName) {
|
|
||||||
super(debugName, SimpleLanguage.INSTANCE);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return "SimpleTokenType." + super.toString();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 3.2. Define an element type
|
### 3.2. Define an element type
|
||||||
|
|
||||||
```java
|
{% include_code simple_language_plugin/src/com/simpleplugin/psi/SimpleElementType.java %}
|
||||||
package com.simpleplugin.psi;
|
|
||||||
|
|
||||||
import com.intellij.psi.tree.IElementType;
|
|
||||||
import com.simpleplugin.SimpleLanguage;
|
|
||||||
import org.jetbrains.annotations.NonNls;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
|
|
||||||
public class SimpleElementType extends IElementType {
|
|
||||||
public SimpleElementType(@NotNull @NonNls String debugName) {
|
|
||||||
super(debugName, SimpleLanguage.INSTANCE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 3.3. Define grammar
|
### 3.3. Define grammar
|
||||||
|
|
||||||
Define a grammar for the properties language with */com/simpleplugin/Simple.bnf* file.
|
Define a grammar for the properties language with */com/simpleplugin/Simple.bnf* file.
|
||||||
|
|
||||||
```java
|
{% include_code simple_language_plugin/src/com/simpleplugin/Simple.bnf lang:java %}
|
||||||
{
|
|
||||||
parserClass="com.simpleplugin.parser.SimpleParser"
|
|
||||||
|
|
||||||
extends="com.intellij.extapi.psi.ASTWrapperPsiElement"
|
|
||||||
|
|
||||||
psiClassPrefix="Simple"
|
|
||||||
psiImplClassSuffix="Impl"
|
|
||||||
psiPackage="com.simpleplugin.psi"
|
|
||||||
psiImplPackage="com.simpleplugin.psi.impl"
|
|
||||||
|
|
||||||
elementTypeHolderClass="com.simpleplugin.psi.SimpleTypes"
|
|
||||||
elementTypeClass="com.simpleplugin.psi.SimpleElementType"
|
|
||||||
tokenTypeClass="com.simpleplugin.psi.SimpleTokenType"
|
|
||||||
}
|
|
||||||
|
|
||||||
simpleFile ::= item_*
|
|
||||||
|
|
||||||
private item_ ::= (property|COMMENT|CRLF)
|
|
||||||
|
|
||||||
property ::= (KEY? SEPARATOR VALUE?) | KEY
|
|
||||||
```
|
|
||||||
|
|
||||||
As you see a properties file can contain properties, comments and line breaks.
|
As you see a properties file can contain properties, comments and line breaks.
|
||||||
|
|
||||||
|
@ -5,19 +5,7 @@ title: 2. Language and File Type
|
|||||||
|
|
||||||
### 2.1. Define a language
|
### 2.1. Define a language
|
||||||
|
|
||||||
```java
|
{% include_code simple_language_plugin/src/com/simpleplugin/Simple.bnf lang:java %}
|
||||||
package com.simpleplugin;
|
|
||||||
|
|
||||||
import com.intellij.lang.Language;
|
|
||||||
|
|
||||||
public class SimpleLanguage extends Language {
|
|
||||||
public static final SimpleLanguage INSTANCE = new SimpleLanguage();
|
|
||||||
|
|
||||||
private SimpleLanguage() {
|
|
||||||
super("Simple");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2.2. Define an icon
|
### 2.2. Define an icon
|
||||||
|
|
||||||
@ -25,78 +13,15 @@ Copy the
|
|||||||
[icon](https://raw.githubusercontent.com/cheptsov/SimplePlugin/master/src/com/simpleplugin/icons/jar-gray.png)
|
[icon](https://raw.githubusercontent.com/cheptsov/SimplePlugin/master/src/com/simpleplugin/icons/jar-gray.png)
|
||||||
to **com.simpleplugin.icons** package.
|
to **com.simpleplugin.icons** package.
|
||||||
|
|
||||||
```java
|
{% include_code simple_language_plugin/src/com/simpleplugin/SimpleIcons.java %}
|
||||||
package com.simpleplugin;
|
|
||||||
|
|
||||||
import com.intellij.openapi.util.IconLoader;
|
|
||||||
|
|
||||||
import javax.swing.*;
|
|
||||||
|
|
||||||
public class SimpleIcons {
|
|
||||||
public static final Icon FILE = IconLoader.getIcon("/com/simpleplugin/icons/jar-gray.png");
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2.3. Define a file type
|
### 2.3. Define a file type
|
||||||
|
|
||||||
```java
|
{% include_code simple_language_plugin/src/com/simpleplugin/SimpleFileType.java %}
|
||||||
package com.simpleplugin;
|
|
||||||
|
|
||||||
import com.intellij.openapi.fileTypes.LanguageFileType;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
|
|
||||||
import javax.swing.*;
|
|
||||||
|
|
||||||
public class SimpleFileType extends LanguageFileType {
|
|
||||||
public static final SimpleFileType INSTANCE = new SimpleFileType();
|
|
||||||
|
|
||||||
private SimpleFileType() {
|
|
||||||
super(SimpleLanguage.INSTANCE);
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
@Override
|
|
||||||
public String getName() {
|
|
||||||
return "Simple file";
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
@Override
|
|
||||||
public String getDescription() {
|
|
||||||
return "Simple language file";
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
@Override
|
|
||||||
public String getDefaultExtension() {
|
|
||||||
return "simple";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
@Override
|
|
||||||
public Icon getIcon() {
|
|
||||||
return SimpleIcons.FILE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2.4. Define a file type factory
|
### 2.4. Define a file type factory
|
||||||
|
|
||||||
```java
|
{% include_code simple_language_plugin/src/com/simpleplugin/SimpleFileTypeFactory.java %}
|
||||||
package com.simpleplugin;
|
|
||||||
|
|
||||||
import com.intellij.openapi.fileTypes.FileTypeConsumer;
|
|
||||||
import com.intellij.openapi.fileTypes.FileTypeFactory;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
|
|
||||||
public class SimpleFileTypeFactory extends FileTypeFactory{
|
|
||||||
@Override
|
|
||||||
public void createFileTypes(@NotNull FileTypeConsumer fileTypeConsumer) {
|
|
||||||
fileTypeConsumer.consume(SimpleFileType.INSTANCE, "simple");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2.5. Register the file type factory
|
### 2.5. Register the file type factory
|
||||||
|
|
||||||
|
@ -9,54 +9,7 @@ The easiest way to create a lexer is to use [JFlex](http://jflex.de/)
|
|||||||
|
|
||||||
Define */com/simpleplugin/Simple.flex* file with rules for our lexer.
|
Define */com/simpleplugin/Simple.flex* file with rules for our lexer.
|
||||||
|
|
||||||
```java
|
{% include_code simple_language_plugin/src/com/simpleplugin/Simple.flex lang:java %}
|
||||||
package com.simpleplugin;
|
|
||||||
|
|
||||||
import com.intellij.lexer.FlexLexer;
|
|
||||||
import com.intellij.psi.tree.IElementType;
|
|
||||||
import com.simpleplugin.psi.SimpleTypes;
|
|
||||||
import com.intellij.psi.TokenType;
|
|
||||||
|
|
||||||
%%
|
|
||||||
|
|
||||||
%class SimpleLexer
|
|
||||||
%implements FlexLexer
|
|
||||||
%unicode
|
|
||||||
%function advance
|
|
||||||
%type IElementType
|
|
||||||
%eof{ return;
|
|
||||||
%eof}
|
|
||||||
|
|
||||||
CRLF= \n|\r|\r\n
|
|
||||||
WHITE_SPACE=[\ \t\f]
|
|
||||||
FIRST_VALUE_CHARACTER=[^ \n\r\f\\] | "\\"{CRLF} | "\\".
|
|
||||||
VALUE_CHARACTER=[^\n\r\f\\] | "\\"{CRLF} | "\\".
|
|
||||||
END_OF_LINE_COMMENT=("#"|"!")[^\r\n]*
|
|
||||||
SEPARATOR=[:=]
|
|
||||||
KEY_CHARACTER=[^:=\ \n\r\t\f\\] | "\\"{CRLF} | "\\".
|
|
||||||
|
|
||||||
%state WAITING_VALUE
|
|
||||||
|
|
||||||
%%
|
|
||||||
|
|
||||||
<YYINITIAL> {END_OF_LINE_COMMENT} { yybegin(YYINITIAL); return SimpleTypes.COMMENT; }
|
|
||||||
|
|
||||||
<YYINITIAL> {KEY_CHARACTER}+ { yybegin(YYINITIAL); return SimpleTypes.KEY; }
|
|
||||||
|
|
||||||
<YYINITIAL> {SEPARATOR} { yybegin(WAITING_VALUE); return SimpleTypes.SEPARATOR; }
|
|
||||||
|
|
||||||
<WAITING_VALUE> {CRLF} { yybegin(YYINITIAL); return SimpleTypes.CRLF; }
|
|
||||||
|
|
||||||
<WAITING_VALUE> {WHITE_SPACE}+ { yybegin(WAITING_VALUE); return TokenType.WHITE_SPACE; }
|
|
||||||
|
|
||||||
<WAITING_VALUE> {FIRST_VALUE_CHARACTER}{VALUE_CHARACTER}* { yybegin(YYINITIAL); return SimpleTypes.VALUE; }
|
|
||||||
|
|
||||||
{CRLF} { yybegin(YYINITIAL); return SimpleTypes.CRLF; }
|
|
||||||
|
|
||||||
{WHITE_SPACE}+ { yybegin(YYINITIAL); return TokenType.WHITE_SPACE; }
|
|
||||||
|
|
||||||
. { return TokenType.BAD_CHARACTER; }
|
|
||||||
```
|
|
||||||
|
|
||||||
### 4.2. Generate a lexer class
|
### 4.2. Generate a lexer class
|
||||||
|
|
||||||
@ -71,130 +24,15 @@ After that the IDE will generate lexer: *com.simpleplugin.SimpleLexer*.
|
|||||||
|
|
||||||
### 4.3. Define an adapter
|
### 4.3. Define an adapter
|
||||||
|
|
||||||
```java
|
{% include_code simple_language_plugin/src/com/simpleplugin/SimpleLexerAdapter.java %}
|
||||||
package com.simpleplugin;
|
|
||||||
|
|
||||||
import com.intellij.lexer.FlexAdapter;
|
|
||||||
|
|
||||||
import java.io.Reader;
|
|
||||||
|
|
||||||
public class SimpleLexerAdapter extends FlexAdapter {
|
|
||||||
public SimpleLexerAdapter() {
|
|
||||||
super(new SimpleLexer((Reader) null));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 4.4. Define a file
|
### 4.4. Define a file
|
||||||
|
|
||||||
```java
|
{% include_code simple_language_plugin/src/com/simpleplugin/psi/SimpleFile.java %}
|
||||||
package com.simpleplugin.psi;
|
|
||||||
|
|
||||||
import com.intellij.extapi.psi.PsiFileBase;
|
|
||||||
import com.intellij.openapi.fileTypes.FileType;
|
|
||||||
import com.intellij.psi.FileViewProvider;
|
|
||||||
import com.simpleplugin.SimpleFileType;
|
|
||||||
import com.simpleplugin.SimpleLanguage;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
|
|
||||||
import javax.swing.*;
|
|
||||||
|
|
||||||
public class SimpleFile extends PsiFileBase {
|
|
||||||
public SimpleFile(@NotNull FileViewProvider viewProvider) {
|
|
||||||
super(viewProvider, SimpleLanguage.INSTANCE);
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
@Override
|
|
||||||
public FileType getFileType() {
|
|
||||||
return SimpleFileType.INSTANCE;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return "Simple File";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Icon getIcon(int flags) {
|
|
||||||
return super.getIcon(flags);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 4.5. Define a parser definition
|
### 4.5. Define a parser definition
|
||||||
|
|
||||||
```java
|
{% include_code simple_language_plugin/src/com/simpleplugin/SimpleParserDefinition.java %}
|
||||||
package com.simpleplugin;
|
|
||||||
|
|
||||||
import com.intellij.lang.ASTNode;
|
|
||||||
import com.intellij.lang.Language;
|
|
||||||
import com.intellij.lang.ParserDefinition;
|
|
||||||
import com.intellij.lang.PsiParser;
|
|
||||||
import com.intellij.lexer.Lexer;
|
|
||||||
import com.intellij.openapi.project.Project;
|
|
||||||
import com.intellij.psi.FileViewProvider;
|
|
||||||
import com.intellij.psi.PsiElement;
|
|
||||||
import com.intellij.psi.PsiFile;
|
|
||||||
import com.intellij.psi.TokenType;
|
|
||||||
import com.intellij.psi.tree.IFileElementType;
|
|
||||||
import com.intellij.psi.tree.TokenSet;
|
|
||||||
import com.simpleplugin.parser.SimpleParser;
|
|
||||||
import com.simpleplugin.psi.SimpleFile;
|
|
||||||
import com.simpleplugin.psi.SimpleTypes;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
|
|
||||||
public class SimpleParserDefinition implements ParserDefinition{
|
|
||||||
public static final TokenSet WHITE_SPACES = TokenSet.create(TokenType.WHITE_SPACE);
|
|
||||||
public static final TokenSet COMMENTS = TokenSet.create(SimpleTypes.COMMENT);
|
|
||||||
|
|
||||||
public static final IFileElementType FILE = new IFileElementType(Language.<SimpleLanguage>findInstance(SimpleLanguage.class));
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
@Override
|
|
||||||
public Lexer createLexer(Project project) {
|
|
||||||
return new SimpleLexerAdapter();
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
public TokenSet getWhitespaceTokens() {
|
|
||||||
return WHITE_SPACES;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
public TokenSet getCommentTokens() {
|
|
||||||
return COMMENTS;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
public TokenSet getStringLiteralElements() {
|
|
||||||
return TokenSet.EMPTY;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
public PsiParser createParser(final Project project) {
|
|
||||||
return new SimpleParser();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public IFileElementType getFileNodeType() {
|
|
||||||
return FILE;
|
|
||||||
}
|
|
||||||
|
|
||||||
public PsiFile createFile(FileViewProvider viewProvider) {
|
|
||||||
return new SimpleFile(viewProvider);
|
|
||||||
}
|
|
||||||
|
|
||||||
public SpaceRequirements spaceExistanceTypeBetweenTokens(ASTNode left, ASTNode right) {
|
|
||||||
return SpaceRequirements.MAY;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
public PsiElement createElement(ASTNode node) {
|
|
||||||
return SimpleTypes.Factory.createElement(node);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 4.6. Register the parser definition
|
### 4.6. Register the parser definition
|
||||||
|
|
||||||
|
@ -9,43 +9,7 @@ These icons may provide navigation to related code.
|
|||||||
|
|
||||||
Let's annotate usages of our properties within Java code and provide navigation to the definition of these properties.
|
Let's annotate usages of our properties within Java code and provide navigation to the definition of these properties.
|
||||||
|
|
||||||
```java
|
{% include_code simple_language_plugin/src/com/simpleplugin/SimpleLineMarkerProvider.java %}
|
||||||
package com.simpleplugin;
|
|
||||||
|
|
||||||
import com.intellij.codeInsight.daemon.RelatedItemLineMarkerInfo;
|
|
||||||
import com.intellij.codeInsight.daemon.RelatedItemLineMarkerProvider;
|
|
||||||
import com.intellij.codeInsight.navigation.NavigationGutterIconBuilder;
|
|
||||||
import com.intellij.openapi.project.Project;
|
|
||||||
import com.intellij.psi.PsiElement;
|
|
||||||
import com.intellij.psi.PsiLiteralExpression;
|
|
||||||
import com.simpleplugin.psi.SimpleProperty;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class SimpleLineMarkerProvider extends RelatedItemLineMarkerProvider {
|
|
||||||
@Override
|
|
||||||
protected void collectNavigationMarkers(@NotNull PsiElement element, Collection<? super RelatedItemLineMarkerInfo> result) {
|
|
||||||
if (element instanceof PsiLiteralExpression) {
|
|
||||||
PsiLiteralExpression literalExpression = (PsiLiteralExpression) element;
|
|
||||||
String value = (String) literalExpression.getValue();
|
|
||||||
if (value != null && value.startsWith("simple:")) {
|
|
||||||
Project project = element.getProject();
|
|
||||||
final List<SimpleProperty> properties = SimpleUtil.findProperties(project, value.substring(7));
|
|
||||||
if (properties.size() > 0) {
|
|
||||||
NavigationGutterIconBuilder<PsiElement> builder =
|
|
||||||
NavigationGutterIconBuilder.create(SimpleIcons.FILE).
|
|
||||||
setTargets(properties).
|
|
||||||
setTooltipText("Navigate to a simple property");
|
|
||||||
result.add(builder.createLineMarkerInfo(element));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
## More technical details for implementers
|
## More technical details for implementers
|
||||||
|
|
||||||
|
@ -9,33 +9,7 @@ If we want to have custom methods in PSI classes we need to define them separate
|
|||||||
|
|
||||||
Let's define an utility class with these helper methods.
|
Let's define an utility class with these helper methods.
|
||||||
|
|
||||||
```java
|
{% include_code simple_language_plugin/src/com/simpleplugin/psi/impl/SimplePsiImplUtil.java %}
|
||||||
package com.simpleplugin.psi.impl;
|
|
||||||
|
|
||||||
import com.intellij.lang.ASTNode;
|
|
||||||
import com.simpleplugin.psi.SimpleProperty;
|
|
||||||
import com.simpleplugin.psi.SimpleTypes;
|
|
||||||
|
|
||||||
public class SimplePsiImplUtil {
|
|
||||||
public static String getKey(SimpleProperty element) {
|
|
||||||
ASTNode keyNode = element.getNode().findChildByType(SimpleTypes.KEY);
|
|
||||||
if (keyNode != null) {
|
|
||||||
return keyNode.getText();
|
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String getValue(SimpleProperty element) {
|
|
||||||
ASTNode valueNode = element.getNode().findChildByType(SimpleTypes.VALUE);
|
|
||||||
if (valueNode != null) {
|
|
||||||
return valueNode.getText();
|
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 6.2. Update grammar and regenerate the parser
|
### 6.2. Update grammar and regenerate the parser
|
||||||
|
|
||||||
@ -75,65 +49,7 @@ After we made our changes to the grammar we can regenerate the parser and PSI cl
|
|||||||
Now we need an utility class to search PSI elements for defined properties over the project.
|
Now we need an utility class to search PSI elements for defined properties over the project.
|
||||||
We will use this utility later when implementing code assistance.
|
We will use this utility later when implementing code assistance.
|
||||||
|
|
||||||
```java
|
{% include_code simple_language_plugin/src/com/simpleplugin/SimpleUtil.java %}
|
||||||
package com.simpleplugin;
|
|
||||||
|
|
||||||
import com.intellij.openapi.project.Project;
|
|
||||||
import com.intellij.openapi.vfs.VirtualFile;
|
|
||||||
import com.intellij.psi.PsiManager;
|
|
||||||
import com.intellij.psi.search.FileTypeIndex;
|
|
||||||
import com.intellij.psi.search.GlobalSearchScope;
|
|
||||||
import com.intellij.psi.util.PsiTreeUtil;
|
|
||||||
import com.intellij.util.indexing.FileBasedIndex;
|
|
||||||
import com.simpleplugin.psi.SimpleFile;
|
|
||||||
import com.simpleplugin.psi.SimpleProperty;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class SimpleUtil {
|
|
||||||
public static List<SimpleProperty> findProperties(Project project, String key) {
|
|
||||||
List<SimpleProperty> result = null;
|
|
||||||
Collection<VirtualFile> virtualFiles = FileBasedIndex.getInstance().getContainingFiles(FileTypeIndex.NAME, SimpleFileType.INSTANCE,
|
|
||||||
GlobalSearchScope.allScope(project));
|
|
||||||
for (VirtualFile virtualFile : virtualFiles) {
|
|
||||||
SimpleFile simpleFile = (SimpleFile) PsiManager.getInstance(project).findFile(virtualFile);
|
|
||||||
if (simpleFile != null) {
|
|
||||||
SimpleProperty[] properties = PsiTreeUtil.getChildrenOfType(simpleFile, SimpleProperty.class);
|
|
||||||
if (properties != null) {
|
|
||||||
for (SimpleProperty property : properties) {
|
|
||||||
if (key.equals(property.getKey())) {
|
|
||||||
if (result == null) {
|
|
||||||
result = new ArrayList<SimpleProperty>();
|
|
||||||
}
|
|
||||||
result.add(property);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result != null ? result : Collections.<SimpleProperty>emptyList();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static List<SimpleProperty> findProperties(Project project) {
|
|
||||||
List<SimpleProperty> result = new ArrayList<SimpleProperty>();
|
|
||||||
Collection<VirtualFile> virtualFiles = FileBasedIndex.getInstance().getContainingFiles(FileTypeIndex.NAME, SimpleFileType.INSTANCE,
|
|
||||||
GlobalSearchScope.allScope(project));
|
|
||||||
for (VirtualFile virtualFile : virtualFiles) {
|
|
||||||
SimpleFile simpleFile = (SimpleFile) PsiManager.getInstance(project).findFile(virtualFile);
|
|
||||||
if (simpleFile != null) {
|
|
||||||
SimpleProperty[] properties = PsiTreeUtil.getChildrenOfType(simpleFile, SimpleProperty.class);
|
|
||||||
if (properties != null) {
|
|
||||||
Collections.addAll(result, properties);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
----------------
|
----------------
|
||||||
[Previous](syntax_highlighter_and_color_settings_page.md)
|
[Previous](syntax_highlighter_and_color_settings_page.md)
|
||||||
|
@ -9,180 +9,17 @@ Let's add a quick fix which helps to define an unresolved property from its usag
|
|||||||
|
|
||||||
### 18.1. Update the element factory
|
### 18.1. Update the element factory
|
||||||
|
|
||||||
```java
|
{% include_code simple_language_plugin/src/com/simpleplugin/psi/SimpleElementFactory.java %}
|
||||||
package com.simpleplugin.psi;
|
|
||||||
|
|
||||||
import com.intellij.openapi.project.Project;
|
|
||||||
import com.intellij.psi.PsiElement;
|
|
||||||
import com.intellij.psi.PsiFileFactory;
|
|
||||||
import com.simpleplugin.SimpleFileType;
|
|
||||||
|
|
||||||
public class SimpleElementFactory {
|
|
||||||
public static SimpleProperty createProperty(Project project, String name, String value) {
|
|
||||||
final SimpleFile file = createFile(project, name + " = " + value);
|
|
||||||
return (SimpleProperty) file.getFirstChild();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static SimpleProperty createProperty(Project project, String name) {
|
|
||||||
final SimpleFile file = createFile(project, name);
|
|
||||||
return (SimpleProperty) file.getFirstChild();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static PsiElement createCRLF(Project project) {
|
|
||||||
final SimpleFile file = createFile(project, "\n");
|
|
||||||
return file.getFirstChild();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static SimpleFile createFile(Project project, String text) {
|
|
||||||
String name = "dummy.simple";
|
|
||||||
return (SimpleFile) PsiFileFactory.getInstance(project).
|
|
||||||
createFileFromText(name, SimpleFileType.INSTANCE, text);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 18.2. Define an intention action
|
### 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.
|
The quick fix will create a property in the file chosen by user, and navigate to this property after creation.
|
||||||
|
|
||||||
```java
|
{% include_code simple_language_plugin/src/com/simpleplugin/CreatePropertyQuickFix.java %}
|
||||||
package com.simpleplugin;
|
|
||||||
|
|
||||||
import com.intellij.codeInsight.intention.impl.BaseIntentionAction;
|
|
||||||
import com.intellij.lang.ASTNode;
|
|
||||||
import com.intellij.openapi.application.ApplicationManager;
|
|
||||||
import com.intellij.openapi.command.WriteCommandAction;
|
|
||||||
import com.intellij.openapi.editor.Editor;
|
|
||||||
import com.intellij.openapi.fileChooser.FileChooser;
|
|
||||||
import com.intellij.openapi.fileChooser.FileChooserDescriptor;
|
|
||||||
import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory;
|
|
||||||
import com.intellij.openapi.fileEditor.FileEditorManager;
|
|
||||||
import com.intellij.openapi.project.Project;
|
|
||||||
import com.intellij.openapi.vfs.VirtualFile;
|
|
||||||
import com.intellij.pom.Navigatable;
|
|
||||||
import com.intellij.psi.PsiFile;
|
|
||||||
import com.intellij.psi.PsiManager;
|
|
||||||
import com.intellij.psi.search.FileTypeIndex;
|
|
||||||
import com.intellij.psi.search.GlobalSearchScope;
|
|
||||||
import com.intellij.util.IncorrectOperationException;
|
|
||||||
import com.intellij.util.indexing.FileBasedIndex;
|
|
||||||
import com.simpleplugin.psi.SimpleElementFactory;
|
|
||||||
import com.simpleplugin.psi.SimpleFile;
|
|
||||||
import com.simpleplugin.psi.SimpleProperty;
|
|
||||||
import com.simpleplugin.psi.SimpleTypes;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
|
|
||||||
import java.util.Collection;
|
|
||||||
|
|
||||||
class CreatePropertyQuickFix extends BaseIntentionAction {
|
|
||||||
private String key;
|
|
||||||
|
|
||||||
CreatePropertyQuickFix(String key) {
|
|
||||||
this.key = key;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
@Override
|
|
||||||
public String getText() {
|
|
||||||
return "Create property";
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
@Override
|
|
||||||
public String getFamilyName() {
|
|
||||||
return "Simple properties";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void invoke(@NotNull final Project project, final Editor editor, PsiFile file) throws IncorrectOperationException {
|
|
||||||
ApplicationManager.getApplication().invokeLater(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
Collection<VirtualFile> virtualFiles = FileBasedIndex.getInstance().getContainingFiles(FileTypeIndex.NAME, SimpleFileType.INSTANCE,
|
|
||||||
GlobalSearchScope.allScope(project));
|
|
||||||
if (virtualFiles.size() == 1) {
|
|
||||||
createProperty(project, virtualFiles.iterator().next());
|
|
||||||
} else {
|
|
||||||
final FileChooserDescriptor descriptor = FileChooserDescriptorFactory.createSingleFileDescriptor(SimpleFileType.INSTANCE);
|
|
||||||
descriptor.setRoots(project.getBaseDir());
|
|
||||||
final VirtualFile file = FileChooser.chooseFile(descriptor, project, null);
|
|
||||||
if (file != null) {
|
|
||||||
createProperty(project, file);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private void createProperty(final Project project, final VirtualFile file) {
|
|
||||||
new WriteCommandAction.Simple(project) {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
SimpleFile simpleFile = (SimpleFile) PsiManager.getInstance(project).findFile(file);
|
|
||||||
ASTNode lastChildNode = simpleFile.getNode().getLastChildNode();
|
|
||||||
if (lastChildNode != null && !lastChildNode.getElementType().equals(SimpleTypes.CRLF)) {
|
|
||||||
simpleFile.getNode().addChild(SimpleElementFactory.createCRLF(project).getNode());
|
|
||||||
}
|
|
||||||
SimpleProperty property = SimpleElementFactory.createProperty(project, key, "");
|
|
||||||
simpleFile.getNode().addChild(property.getNode());
|
|
||||||
((Navigatable) property.getLastChild().getNavigationElement()).navigate(true);
|
|
||||||
FileEditorManager.getInstance(project).getSelectedTextEditor().getCaretModel().
|
|
||||||
moveCaretRelatively(2, 0, false, false, false);
|
|
||||||
}
|
|
||||||
}.execute();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 18.3. Update the annotator
|
### 18.3. Update the annotator
|
||||||
|
|
||||||
```java
|
{% include_code simple_language_plugin/src/com/simpleplugin/SimpleAnnotator.java %}
|
||||||
package com.simpleplugin;
|
|
||||||
|
|
||||||
import com.intellij.lang.annotation.Annotation;
|
|
||||||
import com.intellij.lang.annotation.AnnotationHolder;
|
|
||||||
import com.intellij.lang.annotation.Annotator;
|
|
||||||
import com.intellij.openapi.editor.DefaultLanguageHighlighterColors;
|
|
||||||
import com.intellij.openapi.project.Project;
|
|
||||||
import com.intellij.openapi.util.TextRange;
|
|
||||||
import com.intellij.psi.PsiElement;
|
|
||||||
import com.intellij.psi.PsiLiteralExpression;
|
|
||||||
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 = (String) literalExpression.getValue();
|
|
||||||
if (value != null && value.startsWith("simple:")) {
|
|
||||||
Project project = element.getProject();
|
|
||||||
String key = value.substring(7);
|
|
||||||
List<SimpleProperty> properties = SimpleUtil.findProperties(project, key);
|
|
||||||
if (properties.size() == 1) {
|
|
||||||
TextRange range = new TextRange(element.getTextRange().getStartOffset() + 7,
|
|
||||||
element.getTextRange().getStartOffset() + 7);
|
|
||||||
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());
|
|
||||||
holder.createErrorAnnotation(range, "Unresolved property").
|
|
||||||
registerFix(new CreatePropertyQuickFix(key));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 18.4. Run the project
|
### 18.4. Run the project
|
||||||
|
|
||||||
|
@ -10,29 +10,9 @@ Resolving references means the ability to go from the usage of an element to the
|
|||||||
|
|
||||||
### 10.1. Define a base named element class
|
### 10.1. Define a base named element class
|
||||||
|
|
||||||
```java
|
{% include_code simple_language_plugin/src/com/simpleplugin/psi/SimpleNamedElement.java %}
|
||||||
package com.simpleplugin.psi;
|
|
||||||
|
|
||||||
import com.intellij.psi.PsiNameIdentifierOwner;
|
{% include_code simple_language_plugin/src/com/simpleplugin/psi/impl/SimpleNamedElementImpl.java %}
|
||||||
|
|
||||||
public interface SimpleNamedElement extends PsiNameIdentifierOwner {
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
```java
|
|
||||||
package com.simpleplugin.psi.impl;
|
|
||||||
|
|
||||||
import com.intellij.extapi.psi.ASTWrapperPsiElement;
|
|
||||||
import com.intellij.lang.ASTNode;
|
|
||||||
import com.simpleplugin.psi.SimpleNamedElement;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
|
|
||||||
public abstract class SimpleNamedElementImpl extends ASTWrapperPsiElement implements SimpleNamedElement {
|
|
||||||
public SimpleNamedElementImpl(@NotNull ASTNode node) {
|
|
||||||
super(node);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 10.2. Define helper methods for generated PSI elements
|
### 10.2. Define helper methods for generated PSI elements
|
||||||
|
|
||||||
@ -100,100 +80,14 @@ property ::= (KEY? SEPARATOR VALUE?) | KEY {mixin="com.simpleplugin.psi.impl.Sim
|
|||||||
|
|
||||||
Now we need to define a reference class to resolve a property from it's usage.
|
Now we need to define a reference class to resolve a property from it's usage.
|
||||||
|
|
||||||
```java
|
{% include_code simple_language_plugin/src/com/simpleplugin/SimpleReference.java %}
|
||||||
package com.simpleplugin;
|
|
||||||
|
|
||||||
import com.intellij.codeInsight.lookup.LookupElement;
|
|
||||||
import com.intellij.codeInsight.lookup.LookupElementBuilder;
|
|
||||||
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 org.jetbrains.annotations.Nullable;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class SimpleReference extends PsiReferenceBase<PsiElement> implements PsiPolyVariantReference {
|
|
||||||
private String key;
|
|
||||||
|
|
||||||
public SimpleReference(@NotNull PsiElement element, TextRange textRange) {
|
|
||||||
super(element, textRange);
|
|
||||||
key = element.getText().substring(textRange.getStartOffset(), textRange.getEndOffset());
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
@Override
|
|
||||||
public ResolveResult[] multiResolve(boolean incompleteCode) {
|
|
||||||
Project project = myElement.getProject();
|
|
||||||
final List<SimpleProperty> properties = SimpleUtil.findProperties(project, key);
|
|
||||||
List<ResolveResult> results = new ArrayList<ResolveResult>();
|
|
||||||
for (SimpleProperty property : properties) {
|
|
||||||
results.add(new PsiElementResolveResult(property));
|
|
||||||
}
|
|
||||||
return results.toArray(new ResolveResult[results.size()]);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
@Override
|
|
||||||
public PsiElement resolve() {
|
|
||||||
ResolveResult[] resolveResults = multiResolve(false);
|
|
||||||
return resolveResults.length == 1 ? resolveResults[0].getElement() : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
@Override
|
|
||||||
public Object[] getVariants() {
|
|
||||||
Project project = myElement.getProject();
|
|
||||||
List<SimpleProperty> properties = SimpleUtil.findProperties(project);
|
|
||||||
List<LookupElement> variants = new ArrayList<LookupElement>();
|
|
||||||
for (final SimpleProperty property : properties) {
|
|
||||||
if (property.getKey() != null && property.getKey().length() > 0) {
|
|
||||||
variants.add(LookupElementBuilder.create(property).
|
|
||||||
withIcon(SimpleIcons.FILE).
|
|
||||||
withTypeText(property.getContainingFile().getName())
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return variants.toArray();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 10.6. Define a reference contributor
|
### 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.
|
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.
|
Let's contribute a reference to each usage of a property.
|
||||||
|
|
||||||
```java
|
{% include_code simple_language_plugin/src/com/simpleplugin/SimpleReferenceContributor.java %}
|
||||||
package com.simpleplugin;
|
|
||||||
|
|
||||||
import com.intellij.openapi.util.TextRange;
|
|
||||||
import com.intellij.patterns.PlatformPatterns;
|
|
||||||
import com.intellij.psi.*;
|
|
||||||
import com.intellij.util.ProcessingContext;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
|
|
||||||
public class SimpleReferenceContributor extends PsiReferenceContributor {
|
|
||||||
@Override
|
|
||||||
public void registerReferenceProviders(PsiReferenceRegistrar registrar) {
|
|
||||||
registrar.registerReferenceProvider(PlatformPatterns.psiElement(PsiLiteralExpression.class),
|
|
||||||
new PsiReferenceProvider() {
|
|
||||||
@NotNull
|
|
||||||
@Override
|
|
||||||
public PsiReference[] getReferencesByElement(@NotNull PsiElement element, @NotNull ProcessingContext context) {
|
|
||||||
PsiLiteralExpression literalExpression = (PsiLiteralExpression) element;
|
|
||||||
String text = (String) literalExpression.getValue();
|
|
||||||
if (text != null && text.startsWith("simple:")) {
|
|
||||||
return new PsiReference[]{new SimpleReference(element, new TextRange(8, text.length() + 1))};
|
|
||||||
}
|
|
||||||
return new PsiReference[0];
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 10.7. Register the reference contributor
|
### 10.7. Register the reference contributor
|
||||||
|
|
||||||
@ -215,20 +109,7 @@ As you see the IDE now resolves the property and provides completion.
|
|||||||
|
|
||||||
To allow in-place refactoring we should specify it explicitly in a refactoring support provider.
|
To allow in-place refactoring we should specify it explicitly in a refactoring support provider.
|
||||||
|
|
||||||
```java
|
{% include_code simple_language_plugin/src/com/simpleplugin/SimpleRefactoringSupportProvider.java %}
|
||||||
package com.simpleplugin;
|
|
||||||
|
|
||||||
import com.intellij.lang.refactoring.RefactoringSupportProvider;
|
|
||||||
import com.intellij.psi.PsiElement;
|
|
||||||
import com.simpleplugin.psi.SimpleProperty;
|
|
||||||
|
|
||||||
public class SimpleRefactoringSupportProvider extends RefactoringSupportProvider {
|
|
||||||
@Override
|
|
||||||
public boolean isMemberInplaceRenameAvailable(PsiElement element, PsiElement context) {
|
|
||||||
return element instanceof SimpleProperty;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 10.10. Register the refactoring support provider
|
### 10.10. Register the refactoring support provider
|
||||||
|
|
||||||
|
@ -7,146 +7,15 @@ A structure view factory allows to show the structure of any file in a *Structur
|
|||||||
|
|
||||||
### 14.1. Define a structure view factory
|
### 14.1. Define a structure view factory
|
||||||
|
|
||||||
```java
|
{% include_code simple_language_plugin/src/com/simpleplugin/SimpleStructureViewFactory.java %}
|
||||||
package com.simpleplugin;
|
|
||||||
|
|
||||||
import com.intellij.ide.structureView.StructureViewBuilder;
|
|
||||||
import com.intellij.ide.structureView.StructureViewModel;
|
|
||||||
import com.intellij.ide.structureView.TreeBasedStructureViewBuilder;
|
|
||||||
import com.intellij.lang.PsiStructureViewFactory;
|
|
||||||
import com.intellij.openapi.editor.Editor;
|
|
||||||
import com.intellij.psi.PsiFile;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
|
|
||||||
public class SimpleStructureViewFactory implements PsiStructureViewFactory {
|
|
||||||
@Nullable
|
|
||||||
@Override
|
|
||||||
public StructureViewBuilder getStructureViewBuilder(final PsiFile psiFile) {
|
|
||||||
return new TreeBasedStructureViewBuilder() {
|
|
||||||
@NotNull
|
|
||||||
@Override
|
|
||||||
public StructureViewModel createStructureViewModel(@Nullable Editor editor) {
|
|
||||||
return new SimpleStructureViewModel(psiFile);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 14.2. Define a structure view model
|
### 14.2. Define a structure view model
|
||||||
|
|
||||||
```java
|
{% include_code simple_language_plugin/src/com/simpleplugin/SimpleStructureViewModel.java %}
|
||||||
package com.simpleplugin;
|
|
||||||
|
|
||||||
import com.intellij.ide.structureView.StructureViewModel;
|
|
||||||
import com.intellij.ide.structureView.StructureViewModelBase;
|
|
||||||
import com.intellij.ide.structureView.StructureViewTreeElement;
|
|
||||||
import com.intellij.ide.util.treeView.smartTree.Sorter;
|
|
||||||
import com.intellij.psi.PsiFile;
|
|
||||||
import com.simpleplugin.psi.SimpleFile;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
|
|
||||||
public class SimpleStructureViewModel extends StructureViewModelBase implements
|
|
||||||
StructureViewModel.ElementInfoProvider {
|
|
||||||
public SimpleStructureViewModel(PsiFile psiFile) {
|
|
||||||
super(psiFile, new SimpleStructureViewElement(psiFile));
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
public Sorter[] getSorters() {
|
|
||||||
return new Sorter[] {Sorter.ALPHA_SORTER};
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isAlwaysShowsPlus(StructureViewTreeElement element) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isAlwaysLeaf(StructureViewTreeElement element) {
|
|
||||||
return element instanceof SimpleFile;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 14.3. Define a structure view element
|
### 14.3. Define a structure view element
|
||||||
|
|
||||||
```java
|
{% include_code simple_language_plugin/src/com/simpleplugin/SimpleStructureViewElement.java %}
|
||||||
package com.simpleplugin;
|
|
||||||
|
|
||||||
import com.intellij.ide.structureView.StructureViewTreeElement;
|
|
||||||
import com.intellij.ide.util.treeView.smartTree.SortableTreeElement;
|
|
||||||
import com.intellij.ide.util.treeView.smartTree.TreeElement;
|
|
||||||
import com.intellij.navigation.ItemPresentation;
|
|
||||||
import com.intellij.navigation.NavigationItem;
|
|
||||||
import com.intellij.psi.PsiElement;
|
|
||||||
import com.intellij.psi.PsiNamedElement;
|
|
||||||
import com.intellij.psi.util.PsiTreeUtil;
|
|
||||||
import com.simpleplugin.psi.SimpleFile;
|
|
||||||
import com.simpleplugin.psi.SimpleProperty;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class SimpleStructureViewElement implements StructureViewTreeElement, SortableTreeElement {
|
|
||||||
private PsiElement element;
|
|
||||||
|
|
||||||
public SimpleStructureViewElement(PsiElement element) {
|
|
||||||
this.element = element;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object getValue() {
|
|
||||||
return element;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void navigate(boolean requestFocus) {
|
|
||||||
if (element instanceof NavigationItem) {
|
|
||||||
((NavigationItem) element).navigate(requestFocus);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean canNavigate() {
|
|
||||||
return element instanceof NavigationItem &&
|
|
||||||
((NavigationItem)element).canNavigate();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean canNavigateToSource() {
|
|
||||||
return element instanceof NavigationItem &&
|
|
||||||
((NavigationItem)element).canNavigateToSource();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getAlphaSortKey() {
|
|
||||||
return element instanceof PsiNamedElement ? ((PsiNamedElement) element).getName() : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ItemPresentation getPresentation() {
|
|
||||||
return element instanceof NavigationItem ?
|
|
||||||
((NavigationItem) element).getPresentation() : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public TreeElement[] getChildren() {
|
|
||||||
if (element instanceof SimpleFile) {
|
|
||||||
SimpleProperty[] properties = PsiTreeUtil.getChildrenOfType(element, SimpleProperty.class);
|
|
||||||
List<TreeElement> treeElements = new ArrayList<TreeElement>(properties.length);
|
|
||||||
for (SimpleProperty property : properties) {
|
|
||||||
treeElements.add(new SimpleStructureViewElement(property));
|
|
||||||
}
|
|
||||||
return treeElements.toArray(new TreeElement[treeElements.size()]);
|
|
||||||
} else {
|
|
||||||
return EMPTY_ARRAY;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 14.4. Register the structure view factory
|
### 14.4. Register the structure view factory
|
||||||
|
|
||||||
|
@ -5,80 +5,11 @@ title: 5. Syntax Highlighter and Color Settings Page
|
|||||||
|
|
||||||
### 5.1. Define a syntax highlighter
|
### 5.1. Define a syntax highlighter
|
||||||
|
|
||||||
```java
|
{% include_code simple_language_plugin/src/com/simpleplugin/SimpleSyntaxHighlighter.java %}
|
||||||
package com.simpleplugin;
|
|
||||||
|
|
||||||
import com.intellij.lexer.Lexer;
|
|
||||||
import com.intellij.openapi.editor.DefaultLanguageHighlighterColors;
|
|
||||||
import com.intellij.openapi.editor.HighlighterColors;
|
|
||||||
import com.intellij.openapi.editor.colors.TextAttributesKey;
|
|
||||||
import com.intellij.openapi.fileTypes.SyntaxHighlighterBase;
|
|
||||||
import com.intellij.psi.TokenType;
|
|
||||||
import com.intellij.psi.tree.IElementType;
|
|
||||||
import com.simpleplugin.psi.SimpleTypes;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
|
|
||||||
import static com.intellij.openapi.editor.colors.TextAttributesKey.createTextAttributesKey;
|
|
||||||
|
|
||||||
public class SimpleSyntaxHighlighter extends SyntaxHighlighterBase {
|
|
||||||
public static final TextAttributesKey SEPARATOR = createTextAttributesKey("SIMPLE_SEPARATOR", DefaultLanguageHighlighterColors.OPERATION_SIGN);
|
|
||||||
public static final TextAttributesKey KEY = createTextAttributesKey("SIMPLE_KEY", DefaultLanguageHighlighterColors.KEYWORD);
|
|
||||||
public static final TextAttributesKey VALUE = createTextAttributesKey("SIMPLE_VALUE", DefaultLanguageHighlighterColors.STRING);
|
|
||||||
public static final TextAttributesKey COMMENT = createTextAttributesKey("SIMPLE_COMMENT", DefaultLanguageHighlighterColors.LINE_COMMENT);
|
|
||||||
public static final TextAttributesKey BAD_CHARACTER = createTextAttributesKey("SIMPLE_BAD_CHARACTER", HighlighterColors.BAD_CHARACTER);
|
|
||||||
|
|
||||||
private static final TextAttributesKey[] BAD_CHAR_KEYS = new TextAttributesKey[]{BAD_CHARACTER};
|
|
||||||
private static final TextAttributesKey[] SEPARATOR_KEYS = new TextAttributesKey[]{SEPARATOR};
|
|
||||||
private static final TextAttributesKey[] KEY_KEYS = new TextAttributesKey[]{KEY};
|
|
||||||
private static final TextAttributesKey[] VALUE_KEYS = new TextAttributesKey[]{VALUE};
|
|
||||||
private static final TextAttributesKey[] COMMENT_KEYS = new TextAttributesKey[]{COMMENT};
|
|
||||||
private static final TextAttributesKey[] EMPTY_KEYS = new TextAttributesKey[0];
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
@Override
|
|
||||||
public Lexer getHighlightingLexer() {
|
|
||||||
return new SimpleLexerAdapter();
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
@Override
|
|
||||||
public TextAttributesKey[] getTokenHighlights(IElementType tokenType) {
|
|
||||||
if (tokenType.equals(SimpleTypes.SEPARATOR)) {
|
|
||||||
return SEPARATOR_KEYS;
|
|
||||||
} else if (tokenType.equals(SimpleTypes.KEY)) {
|
|
||||||
return KEY_KEYS;
|
|
||||||
} else if (tokenType.equals(SimpleTypes.VALUE)) {
|
|
||||||
return VALUE_KEYS;
|
|
||||||
} else if (tokenType.equals(SimpleTypes.COMMENT)) {
|
|
||||||
return COMMENT_KEYS;
|
|
||||||
} else if (tokenType.equals(TokenType.BAD_CHARACTER)) {
|
|
||||||
return BAD_CHAR_KEYS;
|
|
||||||
} else {
|
|
||||||
return EMPTY_KEYS;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 5.2. Define a syntax highlighter factory
|
### 5.2. Define a syntax highlighter factory
|
||||||
|
|
||||||
```java
|
{% include_code simple_language_plugin/src/com/simpleplugin/SimpleSyntaxHighlighterFactory.java %}
|
||||||
package com.simpleplugin;
|
|
||||||
|
|
||||||
import com.intellij.openapi.fileTypes.SyntaxHighlighter;
|
|
||||||
import com.intellij.openapi.fileTypes.SyntaxHighlighterFactory;
|
|
||||||
import com.intellij.openapi.project.Project;
|
|
||||||
import com.intellij.openapi.vfs.VirtualFile;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
|
|
||||||
public class SimpleSyntaxHighlighterFactory extends SyntaxHighlighterFactory {
|
|
||||||
@NotNull
|
|
||||||
@Override
|
|
||||||
public SyntaxHighlighter getSyntaxHighlighter(Project project, VirtualFile virtualFile) {
|
|
||||||
return new SimpleSyntaxHighlighter();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 5.3. Register the syntax highlighter factory
|
### 5.3. Register the syntax highlighter factory
|
||||||
|
|
||||||
@ -92,81 +23,7 @@ public class SimpleSyntaxHighlighterFactory extends SyntaxHighlighterFactory {
|
|||||||
|
|
||||||
### 5.5. Define a color settings page
|
### 5.5. Define a color settings page
|
||||||
|
|
||||||
```java
|
{% include_code simple_language_plugin/src/com/simpleplugin/SimpleColorSettingsPage.java %}
|
||||||
package com.simpleplugin;
|
|
||||||
|
|
||||||
import com.intellij.openapi.editor.colors.TextAttributesKey;
|
|
||||||
import com.intellij.openapi.fileTypes.SyntaxHighlighter;
|
|
||||||
import com.intellij.openapi.options.colors.AttributesDescriptor;
|
|
||||||
import com.intellij.openapi.options.colors.ColorDescriptor;
|
|
||||||
import com.intellij.openapi.options.colors.ColorSettingsPage;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
|
|
||||||
import javax.swing.*;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
public class SimpleColorSettingsPage implements ColorSettingsPage {
|
|
||||||
private static final AttributesDescriptor[] DESCRIPTORS = new AttributesDescriptor[]{
|
|
||||||
new AttributesDescriptor("Key", SimpleSyntaxHighlighter.KEY),
|
|
||||||
new AttributesDescriptor("Separator", SimpleSyntaxHighlighter.SEPARATOR),
|
|
||||||
new AttributesDescriptor("Value", SimpleSyntaxHighlighter.VALUE),
|
|
||||||
};
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
@Override
|
|
||||||
public Icon getIcon() {
|
|
||||||
return SimpleIcons.FILE;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
@Override
|
|
||||||
public SyntaxHighlighter getHighlighter() {
|
|
||||||
return new SimpleSyntaxHighlighter();
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
@Override
|
|
||||||
public String getDemoText() {
|
|
||||||
return "# You are reading the \".properties\" entry.\n" +
|
|
||||||
"! The exclamation mark can also mark text as comments.\n" +
|
|
||||||
"website = http://en.wikipedia.org/\n" +
|
|
||||||
"language = English\n" +
|
|
||||||
"# The backslash below tells the application to continue reading\n" +
|
|
||||||
"# the value onto the next line.\n" +
|
|
||||||
"message = Welcome to \\\n" +
|
|
||||||
" Wikipedia!\n" +
|
|
||||||
"# Add spaces to the key\n" +
|
|
||||||
"key\\ with\\ spaces = This is the value that could be looked up with the key \"key with spaces\".\n" +
|
|
||||||
"# Unicode\n" +
|
|
||||||
"tab : \\u0009";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
@Override
|
|
||||||
public Map<String, TextAttributesKey> getAdditionalHighlightingTagToDescriptorMap() {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
@Override
|
|
||||||
public AttributesDescriptor[] getAttributeDescriptors() {
|
|
||||||
return DESCRIPTORS;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
@Override
|
|
||||||
public ColorDescriptor[] getColorDescriptors() {
|
|
||||||
return ColorDescriptor.EMPTY_ARRAY;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
@Override
|
|
||||||
public String getDisplayName() {
|
|
||||||
return "Simple";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 5.6. Register the color settings page
|
### 5.6. Register the color settings page
|
||||||
|
|
||||||
|
@ -27,40 +27,11 @@ tab : \u0009
|
|||||||
|
|
||||||
Create one more file *CompleteTestData.java*.
|
Create one more file *CompleteTestData.java*.
|
||||||
|
|
||||||
```java
|
{% include_code simple_language_plugin/testData/CompleteTestData.java %}
|
||||||
public class Test {
|
|
||||||
public static void main(String[] args) {
|
|
||||||
System.out.println("simple:<caret>");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 3.2. Define a test
|
### 3.2. Define a test
|
||||||
|
|
||||||
```java
|
{% include_code simple_language_plugin/tests/com/simpleplugin/SimpleCodeInsightTest.java %}
|
||||||
package com.simpleplugin;
|
|
||||||
|
|
||||||
import com.intellij.codeInsight.completion.CompletionType;
|
|
||||||
import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase;
|
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class SimpleCodeInsightTest extends LightCodeInsightFixtureTestCase {
|
|
||||||
@Override
|
|
||||||
protected String getTestDataPath() {
|
|
||||||
return "../../SimplePlugin/testData";
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testCompletion() {
|
|
||||||
myFixture.configureByFiles("CompleteTestData.java", "DefaultTestData.simple");
|
|
||||||
myFixture.complete(CompletionType.BASIC, 1);
|
|
||||||
List<String> strings = myFixture.getLookupElementStrings();
|
|
||||||
assertTrue(strings.containsAll(Arrays.asList("key\\ with\\ spaces", "language", "message", "tab", "website")));
|
|
||||||
assertEquals(5, strings.size());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 3.3. Run the test
|
### 3.3. Run the test
|
||||||
|
|
||||||
|
@ -95,36 +95,7 @@ Simple File(0,433)
|
|||||||
|
|
||||||
### 2.4. Define a parsing test
|
### 2.4. Define a parsing test
|
||||||
|
|
||||||
```java
|
{% include_code simple_language_plugin/tests/com/simpleplugin/SimpleParsingTest.java %}
|
||||||
package com.simpleplugin;
|
|
||||||
|
|
||||||
import com.intellij.testFramework.ParsingTestCase;
|
|
||||||
|
|
||||||
public class SimpleParsingTest extends ParsingTestCase {
|
|
||||||
public SimpleParsingTest() {
|
|
||||||
super("", "simple", new SimpleParserDefinition());
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testParsingTestData() {
|
|
||||||
doTest(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected String getTestDataPath() {
|
|
||||||
return "../../SimplePlugin/testData";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected boolean skipSpaces() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected boolean includeRanges() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2.5. Run the test
|
### 2.5. Run the test
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user