Reference code-samples directly (as per DRY)

This commit is contained in:
breandan 2015-12-21 23:35:54 -05:00
parent 80a4c16b4a
commit b940673f47
20 changed files with 65 additions and 1540 deletions

View File

@ -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!

View File

@ -78,68 +78,9 @@ Create the following directory structure:
![Gradle directory structure](img/gradle_directory_structure.png) ![Gradle directory structure](img/gradle_directory_structure.png)
```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:

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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.

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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