From e2cbdf4b3bd48dedc6b5f1f6dd9da5985ff6ea89 Mon Sep 17 00:00:00 2001 From: cheptsov Date: Fri, 11 Jan 2013 19:11:01 +0400 Subject: [PATCH] Initial commit --- .idea/.name | 1 + .idea/compiler.xml | 23 + .idea/encodings.xml | 5 + .idea/misc.xml | 13 + .idea/modules.xml | 9 + .idea/scopes/scope_settings.xml | 5 + .idea/vcs.xml | 7 + META-INF/plugin.xml | 59 ++ SimplePlugin.iml | 14 + gen/com/simpleplugin/parser/SimpleParser.java | 143 ++++ gen/com/simpleplugin/psi/SimpleProperty.java | 23 + gen/com/simpleplugin/psi/SimpleTypes.java | 28 + gen/com/simpleplugin/psi/SimpleVisitor.java | 22 + .../psi/impl/SimplePropertyImpl.java | 49 ++ src/com/simpleplugin/Simple.bnf | 24 + src/com/simpleplugin/Simple.flex | 46 ++ src/com/simpleplugin/SimpleAnnotator.java | 44 + src/com/simpleplugin/SimpleBlock.java | 57 ++ .../SimpleChooseByNameContributor.java | 33 + .../simpleplugin/SimpleCodeStyleSettings.java | 10 + .../SimpleCodeStyleSettingsProvider.java | 47 ++ .../simpleplugin/SimpleColorSettingsPage.java | 73 ++ src/com/simpleplugin/SimpleCommenter.java | 36 + .../SimpleCompletionContributor.java | 23 + src/com/simpleplugin/SimpleFileType.java | 39 + .../simpleplugin/SimpleFileTypeFactory.java | 12 + .../SimpleFindUsagesProvider.java | 69 ++ .../simpleplugin/SimpleFoldingBuilder.java | 60 ++ .../SimpleFormattingModelBuilder.java | 33 + src/com/simpleplugin/SimpleIcons.java | 9 + src/com/simpleplugin/SimpleLanguage.java | 11 + ...mpleLanguageCodeStyleSettingsProvider.java | 42 + src/com/simpleplugin/SimpleLexer.java | 518 ++++++++++++ .../SimpleLineMarkerProvider.java | 34 + .../simpleplugin/SimpleParserDefinition.java | 72 ++ .../SimpleRefactoringSupportProvider.java | 12 + src/com/simpleplugin/SimpleReference.java | 58 ++ .../SimpleReferenceContributor.java | 26 + .../SimpleStructureViewElement.java | 72 ++ .../SimpleStructureViewFactory.java | 23 + .../SimpleStructureViewModel.java | 32 + .../simpleplugin/SimpleSyntaxHighlighter.java | 58 ++ .../SimpleSyntaxHighlighterFactory.java | 15 + src/com/simpleplugin/SimpleTokenType.java | 17 + src/com/simpleplugin/SimpleUtil.java | 57 ++ src/com/simpleplugin/icons/jar-gray.png | Bin 0 -> 729 bytes .../parser/GeneratedParserUtilBase.java | 766 ++++++++++++++++++ .../psi/SimpleElementFactory.java | 18 + .../simpleplugin/psi/SimpleElementType.java | 12 + src/com/simpleplugin/psi/SimpleFile.java | 32 + .../simpleplugin/psi/SimpleNamedElement.java | 6 + src/com/simpleplugin/psi/SimpleTokenType.java | 17 + .../psi/impl/SimpleNamedElementImpl.java | 12 + .../psi/impl/SimplePsiImplUtil.java | 78 ++ 54 files changed, 3004 insertions(+) create mode 100644 .idea/.name create mode 100644 .idea/compiler.xml create mode 100644 .idea/encodings.xml create mode 100644 .idea/misc.xml create mode 100644 .idea/modules.xml create mode 100644 .idea/scopes/scope_settings.xml create mode 100644 .idea/vcs.xml create mode 100644 META-INF/plugin.xml create mode 100644 SimplePlugin.iml create mode 100644 gen/com/simpleplugin/parser/SimpleParser.java create mode 100644 gen/com/simpleplugin/psi/SimpleProperty.java create mode 100644 gen/com/simpleplugin/psi/SimpleTypes.java create mode 100644 gen/com/simpleplugin/psi/SimpleVisitor.java create mode 100644 gen/com/simpleplugin/psi/impl/SimplePropertyImpl.java create mode 100644 src/com/simpleplugin/Simple.bnf create mode 100644 src/com/simpleplugin/Simple.flex create mode 100644 src/com/simpleplugin/SimpleAnnotator.java create mode 100644 src/com/simpleplugin/SimpleBlock.java create mode 100644 src/com/simpleplugin/SimpleChooseByNameContributor.java create mode 100644 src/com/simpleplugin/SimpleCodeStyleSettings.java create mode 100644 src/com/simpleplugin/SimpleCodeStyleSettingsProvider.java create mode 100644 src/com/simpleplugin/SimpleColorSettingsPage.java create mode 100644 src/com/simpleplugin/SimpleCommenter.java create mode 100644 src/com/simpleplugin/SimpleCompletionContributor.java create mode 100644 src/com/simpleplugin/SimpleFileType.java create mode 100644 src/com/simpleplugin/SimpleFileTypeFactory.java create mode 100644 src/com/simpleplugin/SimpleFindUsagesProvider.java create mode 100644 src/com/simpleplugin/SimpleFoldingBuilder.java create mode 100644 src/com/simpleplugin/SimpleFormattingModelBuilder.java create mode 100644 src/com/simpleplugin/SimpleIcons.java create mode 100644 src/com/simpleplugin/SimpleLanguage.java create mode 100644 src/com/simpleplugin/SimpleLanguageCodeStyleSettingsProvider.java create mode 100644 src/com/simpleplugin/SimpleLexer.java create mode 100644 src/com/simpleplugin/SimpleLineMarkerProvider.java create mode 100644 src/com/simpleplugin/SimpleParserDefinition.java create mode 100644 src/com/simpleplugin/SimpleRefactoringSupportProvider.java create mode 100644 src/com/simpleplugin/SimpleReference.java create mode 100644 src/com/simpleplugin/SimpleReferenceContributor.java create mode 100644 src/com/simpleplugin/SimpleStructureViewElement.java create mode 100644 src/com/simpleplugin/SimpleStructureViewFactory.java create mode 100644 src/com/simpleplugin/SimpleStructureViewModel.java create mode 100644 src/com/simpleplugin/SimpleSyntaxHighlighter.java create mode 100644 src/com/simpleplugin/SimpleSyntaxHighlighterFactory.java create mode 100644 src/com/simpleplugin/SimpleTokenType.java create mode 100644 src/com/simpleplugin/SimpleUtil.java create mode 100644 src/com/simpleplugin/icons/jar-gray.png create mode 100644 src/com/simpleplugin/parser/GeneratedParserUtilBase.java create mode 100644 src/com/simpleplugin/psi/SimpleElementFactory.java create mode 100644 src/com/simpleplugin/psi/SimpleElementType.java create mode 100644 src/com/simpleplugin/psi/SimpleFile.java create mode 100644 src/com/simpleplugin/psi/SimpleNamedElement.java create mode 100644 src/com/simpleplugin/psi/SimpleTokenType.java create mode 100644 src/com/simpleplugin/psi/impl/SimpleNamedElementImpl.java create mode 100644 src/com/simpleplugin/psi/impl/SimplePsiImplUtil.java diff --git a/.idea/.name b/.idea/.name new file mode 100644 index 000000000..316279c80 --- /dev/null +++ b/.idea/.name @@ -0,0 +1 @@ +SimplePlugin \ No newline at end of file diff --git a/.idea/compiler.xml b/.idea/compiler.xml new file mode 100644 index 000000000..217af471a --- /dev/null +++ b/.idea/compiler.xml @@ -0,0 +1,23 @@ + + + + + + diff --git a/.idea/encodings.xml b/.idea/encodings.xml new file mode 100644 index 000000000..e206d70d8 --- /dev/null +++ b/.idea/encodings.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 000000000..7e1512672 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 000000000..c6008778a --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/.idea/scopes/scope_settings.xml b/.idea/scopes/scope_settings.xml new file mode 100644 index 000000000..922003b84 --- /dev/null +++ b/.idea/scopes/scope_settings.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 000000000..275077f82 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/META-INF/plugin.xml b/META-INF/plugin.xml new file mode 100644 index 000000000..d1c514eb8 --- /dev/null +++ b/META-INF/plugin.xml @@ -0,0 +1,59 @@ + + com.yourcompany.unique.plugin.id + Plugin display name here + 1.0 + YourCompany + + + most HTML tags may be used + ]]> + + + most HTML tags may be used + ]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/SimplePlugin.iml b/SimplePlugin.iml new file mode 100644 index 000000000..f66860322 --- /dev/null +++ b/SimplePlugin.iml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/gen/com/simpleplugin/parser/SimpleParser.java b/gen/com/simpleplugin/parser/SimpleParser.java new file mode 100644 index 000000000..067b2bb80 --- /dev/null +++ b/gen/com/simpleplugin/parser/SimpleParser.java @@ -0,0 +1,143 @@ +// This is a generated file. Not intended for manual editing. +package com.simpleplugin.parser; + +import org.jetbrains.annotations.*; +import com.intellij.lang.LighterASTNode; +import com.intellij.lang.PsiBuilder; +import com.intellij.lang.PsiBuilder.Marker; +import com.intellij.openapi.diagnostic.Logger; +import static com.simpleplugin.psi.SimpleTypes.*; +import static com.simpleplugin.parser.GeneratedParserUtilBase.*; +import com.intellij.psi.tree.IElementType; +import com.intellij.lang.ASTNode; +import com.intellij.psi.tree.TokenSet; +import com.intellij.lang.PsiParser; + +@SuppressWarnings({"SimplifiableIfStatement", "UnusedAssignment"}) +public class SimpleParser implements PsiParser { + + public static Logger LOG_ = Logger.getInstance("com.simpleplugin.parser.SimpleParser"); + + @NotNull + public ASTNode parse(IElementType root_, PsiBuilder builder_) { + int level_ = 0; + boolean result_; + builder_ = adapt_builder_(root_, builder_, this); + if (root_ == PROPERTY) { + result_ = property(builder_, level_ + 1); + } + else { + Marker marker_ = builder_.mark(); + result_ = parse_root_(root_, builder_, level_); + while (builder_.getTokenType() != null) { + builder_.advanceLexer(); + } + marker_.done(root_); + } + return builder_.getTreeBuilt(); + } + + protected boolean parse_root_(final IElementType root_, final PsiBuilder builder_, final int level_) { + return simpleFile(builder_, level_ + 1); + } + + /* ********************************************************** */ + // (property|COMMENT|CRLF) + static boolean item_(PsiBuilder builder_, int level_) { + if (!recursion_guard_(builder_, level_, "item_")) return false; + return item__0(builder_, level_ + 1); + } + + // property|COMMENT|CRLF + private static boolean item__0(PsiBuilder builder_, int level_) { + if (!recursion_guard_(builder_, level_, "item__0")) return false; + boolean result_ = false; + Marker marker_ = builder_.mark(); + result_ = property(builder_, level_ + 1); + if (!result_) result_ = consumeToken(builder_, COMMENT); + if (!result_) result_ = consumeToken(builder_, CRLF); + if (!result_) { + marker_.rollbackTo(); + } + else { + marker_.drop(); + } + return result_; + } + + /* ********************************************************** */ + // (KEY? SEPARATOR VALUE?) | KEY + public static boolean property(PsiBuilder builder_, int level_) { + if (!recursion_guard_(builder_, level_, "property")) return false; + if (!nextTokenIs(builder_, KEY) && !nextTokenIs(builder_, SEPARATOR) + && replaceVariants(builder_, 2, "")) return false; + boolean result_ = false; + Marker marker_ = builder_.mark(); + enterErrorRecordingSection(builder_, level_, _SECTION_GENERAL_, ""); + result_ = property_0(builder_, level_ + 1); + if (!result_) result_ = consumeToken(builder_, KEY); + if (result_) { + marker_.done(PROPERTY); + } + else { + marker_.rollbackTo(); + } + result_ = exitErrorRecordingSection(builder_, level_, result_, false, _SECTION_GENERAL_, null); + return result_; + } + + // (KEY? SEPARATOR VALUE?) + private static boolean property_0(PsiBuilder builder_, int level_) { + if (!recursion_guard_(builder_, level_, "property_0")) return false; + return property_0_0(builder_, level_ + 1); + } + + // KEY? SEPARATOR VALUE? + private static boolean property_0_0(PsiBuilder builder_, int level_) { + if (!recursion_guard_(builder_, level_, "property_0_0")) return false; + boolean result_ = false; + Marker marker_ = builder_.mark(); + result_ = property_0_0_0(builder_, level_ + 1); + result_ = result_ && consumeToken(builder_, SEPARATOR); + result_ = result_ && property_0_0_2(builder_, level_ + 1); + if (!result_) { + marker_.rollbackTo(); + } + else { + marker_.drop(); + } + return result_; + } + + // KEY? + private static boolean property_0_0_0(PsiBuilder builder_, int level_) { + if (!recursion_guard_(builder_, level_, "property_0_0_0")) return false; + consumeToken(builder_, KEY); + return true; + } + + // VALUE? + private static boolean property_0_0_2(PsiBuilder builder_, int level_) { + if (!recursion_guard_(builder_, level_, "property_0_0_2")) return false; + consumeToken(builder_, VALUE); + return true; + } + + /* ********************************************************** */ + // item_* + static boolean simpleFile(PsiBuilder builder_, int level_) { + if (!recursion_guard_(builder_, level_, "simpleFile")) return false; + int offset_ = builder_.getCurrentOffset(); + while (true) { + if (!item_(builder_, level_ + 1)) break; + int next_offset_ = builder_.getCurrentOffset(); + if (offset_ == next_offset_) { + empty_element_parsed_guard_(builder_, offset_, "simpleFile"); + break; + } + offset_ = next_offset_; + } + return true; + } + +} diff --git a/gen/com/simpleplugin/psi/SimpleProperty.java b/gen/com/simpleplugin/psi/SimpleProperty.java new file mode 100644 index 000000000..2ff5df042 --- /dev/null +++ b/gen/com/simpleplugin/psi/SimpleProperty.java @@ -0,0 +1,23 @@ +// This is a generated file. Not intended for manual editing. +package com.simpleplugin.psi; + +import java.util.List; +import org.jetbrains.annotations.*; +import com.intellij.psi.PsiElement; +import com.intellij.navigation.ItemPresentation; + +public interface SimpleProperty extends SimpleNamedElement { + + String getKey(); + + String getValue(); + + String getName(); + + PsiElement setName(String newName); + + PsiElement getNameIdentifier(); + + ItemPresentation getPresentation(); + +} diff --git a/gen/com/simpleplugin/psi/SimpleTypes.java b/gen/com/simpleplugin/psi/SimpleTypes.java new file mode 100644 index 000000000..36792334c --- /dev/null +++ b/gen/com/simpleplugin/psi/SimpleTypes.java @@ -0,0 +1,28 @@ +// This is a generated file. Not intended for manual editing. +package com.simpleplugin.psi; + +import com.intellij.psi.tree.IElementType; +import com.intellij.psi.PsiElement; +import com.intellij.lang.ASTNode; +import com.simpleplugin.psi.impl.*; + +public interface SimpleTypes { + + IElementType PROPERTY = new SimpleElementType("PROPERTY"); + + IElementType COMMENT = new SimpleTokenType("COMMENT"); + IElementType CRLF = new SimpleTokenType("CRLF"); + IElementType KEY = new SimpleTokenType("KEY"); + IElementType SEPARATOR = new SimpleTokenType("SEPARATOR"); + IElementType VALUE = new SimpleTokenType("VALUE"); + + class Factory { + public static PsiElement createElement(ASTNode node) { + IElementType type = node.getElementType(); + if (type == PROPERTY) { + return new SimplePropertyImpl(node); + } + throw new AssertionError("Unknown element type: " + type); + } + } +} diff --git a/gen/com/simpleplugin/psi/SimpleVisitor.java b/gen/com/simpleplugin/psi/SimpleVisitor.java new file mode 100644 index 000000000..504829968 --- /dev/null +++ b/gen/com/simpleplugin/psi/SimpleVisitor.java @@ -0,0 +1,22 @@ +// This is a generated file. Not intended for manual editing. +package com.simpleplugin.psi; + +import org.jetbrains.annotations.*; +import com.intellij.psi.PsiElementVisitor; +import com.intellij.psi.PsiElement; + +public class SimpleVisitor extends PsiElementVisitor { + + public void visitProperty(@NotNull SimpleProperty o) { + visitNamedElement(o); + } + + public void visitNamedElement(@NotNull SimpleNamedElement o) { + visitPsiElement(o); + } + + public void visitPsiElement(@NotNull PsiElement o) { + visitElement(o); + } + +} diff --git a/gen/com/simpleplugin/psi/impl/SimplePropertyImpl.java b/gen/com/simpleplugin/psi/impl/SimplePropertyImpl.java new file mode 100644 index 000000000..7c0abca71 --- /dev/null +++ b/gen/com/simpleplugin/psi/impl/SimplePropertyImpl.java @@ -0,0 +1,49 @@ +// This is a generated file. Not intended for manual editing. +package com.simpleplugin.psi.impl; + +import java.util.List; +import org.jetbrains.annotations.*; +import com.intellij.lang.ASTNode; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiElementVisitor; +import com.intellij.psi.util.PsiTreeUtil; +import static com.simpleplugin.psi.SimpleTypes.*; +import com.simpleplugin.psi.*; +import com.intellij.navigation.ItemPresentation; + +public class SimplePropertyImpl extends SimpleNamedElementImpl implements SimpleProperty { + + public SimplePropertyImpl(ASTNode node) { + super(node); + } + + public void accept(@NotNull PsiElementVisitor visitor) { + if (visitor instanceof SimpleVisitor) ((SimpleVisitor)visitor).visitProperty(this); + else super.accept(visitor); + } + + public String getKey() { + return SimplePsiImplUtil.getKey(this); + } + + public String getValue() { + return SimplePsiImplUtil.getValue(this); + } + + public String getName() { + return SimplePsiImplUtil.getName(this); + } + + public PsiElement setName(String newName) { + return SimplePsiImplUtil.setName(this, newName); + } + + public PsiElement getNameIdentifier() { + return SimplePsiImplUtil.getNameIdentifier(this); + } + + public ItemPresentation getPresentation() { + return SimplePsiImplUtil.getPresentation(this); + } + +} diff --git a/src/com/simpleplugin/Simple.bnf b/src/com/simpleplugin/Simple.bnf new file mode 100644 index 000000000..c46edce67 --- /dev/null +++ b/src/com/simpleplugin/Simple.bnf @@ -0,0 +1,24 @@ +{ + parserClass="com.simpleplugin.parser.SimpleParser" + stubParserClass="com.simpleplugin.parser.GeneratedParserUtilBase" + + 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" + + psiImplUtilClass="com.simpleplugin.psi.impl.SimplePsiImplUtil" +} + +simpleFile ::= item_* + +private item_ ::= (property|COMMENT|CRLF) + +property ::= (KEY? SEPARATOR VALUE?) | KEY {mixin="com.simpleplugin.psi.impl.SimpleNamedElementImpl" + implements="com.simpleplugin.psi.SimpleNamedElement" methods=[getKey getValue getName setName getNameIdentifier getPresentation]} \ No newline at end of file diff --git a/src/com/simpleplugin/Simple.flex b/src/com/simpleplugin/Simple.flex new file mode 100644 index 000000000..f609f02f5 --- /dev/null +++ b/src/com/simpleplugin/Simple.flex @@ -0,0 +1,46 @@ +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 + +%% + + {END_OF_LINE_COMMENT} { yybegin(YYINITIAL); return SimpleTypes.COMMENT; } + + {KEY_CHARACTER}+ { yybegin(YYINITIAL); return SimpleTypes.KEY; } + + {SEPARATOR} { yybegin(WAITING_VALUE); return SimpleTypes.SEPARATOR; } + + {CRLF} { yybegin(YYINITIAL); return SimpleTypes.CRLF; } + + {WHITE_SPACE}+ { yybegin(WAITING_VALUE); return TokenType.WHITE_SPACE; } + + {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; } \ No newline at end of file diff --git a/src/com/simpleplugin/SimpleAnnotator.java b/src/com/simpleplugin/SimpleAnnotator.java new file mode 100644 index 000000000..c39c49e17 --- /dev/null +++ b/src/com/simpleplugin/SimpleAnnotator.java @@ -0,0 +1,44 @@ +package com.simpleplugin; + +import com.intellij.codeInsight.intention.IntentionAction; +import com.intellij.lang.annotation.Annotation; +import com.intellij.lang.annotation.AnnotationHolder; +import com.intellij.lang.annotation.Annotator; +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.editor.SyntaxHighlighterColors; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.TextRange; +import com.intellij.psi.PsiDocumentManager; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiFile; +import com.intellij.psi.PsiLiteralExpression; +import com.intellij.util.IncorrectOperationException; +import com.simpleplugin.psi.SimpleProperty; +import org.intellij.lang.regexp.intention.CheckRegExpIntentionAction; +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(); + List properties = SimpleUtil.findProperties(project, value.substring(7)); + if (properties.size() == 1) { + TextRange range = new TextRange(element.getTextRange().getStartOffset() + 7, + element.getTextRange().getStartOffset() + 7); + Annotation annotation = holder.createInfoAnnotation(range, null); + annotation.setTextAttributes(SyntaxHighlighterColors.LINE_COMMENT); + } else if (properties.size() == 0) { + TextRange range = new TextRange(element.getTextRange().getStartOffset() + 8, + element.getTextRange().getEndOffset()); + holder.createErrorAnnotation(range, "Unresolved property"); + } + } + } + } +} \ No newline at end of file diff --git a/src/com/simpleplugin/SimpleBlock.java b/src/com/simpleplugin/SimpleBlock.java new file mode 100644 index 000000000..1a4d02c2c --- /dev/null +++ b/src/com/simpleplugin/SimpleBlock.java @@ -0,0 +1,57 @@ +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 buildChildren() { + List blocks = new ArrayList(); + 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; + } +} \ No newline at end of file diff --git a/src/com/simpleplugin/SimpleChooseByNameContributor.java b/src/com/simpleplugin/SimpleChooseByNameContributor.java new file mode 100644 index 000000000..1ec9f06ed --- /dev/null +++ b/src/com/simpleplugin/SimpleChooseByNameContributor.java @@ -0,0 +1,33 @@ +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 properties = SimpleUtil.findProperties(project); + List names = new ArrayList(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 properties = SimpleUtil.findProperties(project, name); + return properties.toArray(new NavigationItem[properties.size()]); + } +} \ No newline at end of file diff --git a/src/com/simpleplugin/SimpleCodeStyleSettings.java b/src/com/simpleplugin/SimpleCodeStyleSettings.java new file mode 100644 index 000000000..bb62cdb4c --- /dev/null +++ b/src/com/simpleplugin/SimpleCodeStyleSettings.java @@ -0,0 +1,10 @@ +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); + } +} \ No newline at end of file diff --git a/src/com/simpleplugin/SimpleCodeStyleSettingsProvider.java b/src/com/simpleplugin/SimpleCodeStyleSettingsProvider.java new file mode 100644 index 000000000..f19dc35b8 --- /dev/null +++ b/src/com/simpleplugin/SimpleCodeStyleSettingsProvider.java @@ -0,0 +1,47 @@ +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); + } + } +} \ No newline at end of file diff --git a/src/com/simpleplugin/SimpleColorSettingsPage.java b/src/com/simpleplugin/SimpleColorSettingsPage.java new file mode 100644 index 000000000..489aede15 --- /dev/null +++ b/src/com/simpleplugin/SimpleColorSettingsPage.java @@ -0,0 +1,73 @@ +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 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"; + } +} \ No newline at end of file diff --git a/src/com/simpleplugin/SimpleCommenter.java b/src/com/simpleplugin/SimpleCommenter.java new file mode 100644 index 000000000..f9f0e24a1 --- /dev/null +++ b/src/com/simpleplugin/SimpleCommenter.java @@ -0,0 +1,36 @@ +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; + } +} \ No newline at end of file diff --git a/src/com/simpleplugin/SimpleCompletionContributor.java b/src/com/simpleplugin/SimpleCompletionContributor.java new file mode 100644 index 000000000..1ca45e01e --- /dev/null +++ b/src/com/simpleplugin/SimpleCompletionContributor.java @@ -0,0 +1,23 @@ +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() { + public void addCompletions(@NotNull CompletionParameters parameters, + ProcessingContext context, + @NotNull CompletionResultSet resultSet) { + resultSet.addElement(LookupElementBuilder.create("Hello")); + } + } + ); + } +} diff --git a/src/com/simpleplugin/SimpleFileType.java b/src/com/simpleplugin/SimpleFileType.java new file mode 100644 index 000000000..3d3714b4d --- /dev/null +++ b/src/com/simpleplugin/SimpleFileType.java @@ -0,0 +1,39 @@ +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; + } +} \ No newline at end of file diff --git a/src/com/simpleplugin/SimpleFileTypeFactory.java b/src/com/simpleplugin/SimpleFileTypeFactory.java new file mode 100644 index 000000000..2a09d45be --- /dev/null +++ b/src/com/simpleplugin/SimpleFileTypeFactory.java @@ -0,0 +1,12 @@ +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"); + } +} \ No newline at end of file diff --git a/src/com/simpleplugin/SimpleFindUsagesProvider.java b/src/com/simpleplugin/SimpleFindUsagesProvider.java new file mode 100644 index 000000000..cb441ddae --- /dev/null +++ b/src/com/simpleplugin/SimpleFindUsagesProvider.java @@ -0,0 +1,69 @@ +package com.simpleplugin; + +import com.intellij.find.impl.HelpID; +import com.intellij.lang.cacheBuilder.DefaultWordsScanner; +import com.intellij.lang.cacheBuilder.WordsScanner; +import com.intellij.lang.findUsages.FindUsagesProvider; +import com.intellij.lexer.FlexAdapter; +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; + +import java.io.Reader; + +public class SimpleFindUsagesProvider implements FindUsagesProvider { + private static final DefaultWordsScanner WORDS_SCANNER = + new DefaultWordsScanner(new FlexAdapter(new SimpleLexer((Reader) null)), + TokenSet.create(SimpleTypes.KEY), TokenSet.create(SimpleTypes.COMMENT), TokenSet.EMPTY); + + @Nullable + @Override + public WordsScanner getWordsScanner() { + return WORDS_SCANNER; + } + + @Override + public boolean canFindUsagesFor(@NotNull PsiElement psiElement) { + return psiElement instanceof PsiNamedElement; + } + + @Nullable + @Override + public String getHelpId(@NotNull PsiElement psiElement) { + return HelpID.FIND_OTHER_USAGES; + } + + @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 ""; + } + } +} \ No newline at end of file diff --git a/src/com/simpleplugin/SimpleFoldingBuilder.java b/src/com/simpleplugin/SimpleFoldingBuilder.java new file mode 100644 index 000000000..8b2acb082 --- /dev/null +++ b/src/com/simpleplugin/SimpleFoldingBuilder.java @@ -0,0 +1,60 @@ +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 descriptors = new ArrayList(); + Collection 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 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; + } +} \ No newline at end of file diff --git a/src/com/simpleplugin/SimpleFormattingModelBuilder.java b/src/com/simpleplugin/SimpleFormattingModelBuilder.java new file mode 100644 index 000000000..61932dd33 --- /dev/null +++ b/src/com/simpleplugin/SimpleFormattingModelBuilder.java @@ -0,0 +1,33 @@ +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). + 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; + } +} \ No newline at end of file diff --git a/src/com/simpleplugin/SimpleIcons.java b/src/com/simpleplugin/SimpleIcons.java new file mode 100644 index 000000000..2719cf69e --- /dev/null +++ b/src/com/simpleplugin/SimpleIcons.java @@ -0,0 +1,9 @@ +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"); +} \ No newline at end of file diff --git a/src/com/simpleplugin/SimpleLanguage.java b/src/com/simpleplugin/SimpleLanguage.java new file mode 100644 index 000000000..425e59019 --- /dev/null +++ b/src/com/simpleplugin/SimpleLanguage.java @@ -0,0 +1,11 @@ +package com.simpleplugin; + +import com.intellij.lang.Language; + +public class SimpleLanguage extends Language { + public static final SimpleLanguage INSTANCE = new SimpleLanguage(); + + private SimpleLanguage() { + super("Simple"); + } +} \ No newline at end of file diff --git a/src/com/simpleplugin/SimpleLanguageCodeStyleSettingsProvider.java b/src/com/simpleplugin/SimpleLanguageCodeStyleSettingsProvider.java new file mode 100644 index 000000000..dd1b6f1dd --- /dev/null +++ b/src/com/simpleplugin/SimpleLanguageCodeStyleSettingsProvider.java @@ -0,0 +1,42 @@ +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"; + } +} \ No newline at end of file diff --git a/src/com/simpleplugin/SimpleLexer.java b/src/com/simpleplugin/SimpleLexer.java new file mode 100644 index 000000000..ac288dcf8 --- /dev/null +++ b/src/com/simpleplugin/SimpleLexer.java @@ -0,0 +1,518 @@ +/* The following code was generated by JFlex 1.4.3 on 1/10/13 12:27 PM */ + +package com.simpleplugin; + +import com.intellij.lexer.FlexLexer; +import com.intellij.psi.tree.IElementType; +import com.simpleplugin.psi.SimpleTypes; +import com.intellij.psi.TokenType; + + +/** + * This class is a scanner generated by + * JFlex 1.4.3 + * on 1/10/13 12:27 PM from the specification file + * /Users/jetbrains/SimplePlugin/src/com/simpleplugin/Simple.flex + */ +class SimpleLexer implements FlexLexer { + /** initial size of the lookahead buffer */ + private static final int ZZ_BUFFERSIZE = 16384; + + /** lexical states */ + public static final int WAITING_VALUE = 2; + public static final int YYINITIAL = 0; + + /** + * ZZ_LEXSTATE[l] is the state in the DFA for the lexical state l + * ZZ_LEXSTATE[l+1] is the state in the DFA for the lexical state l + * at the beginning of a line + * l is of the form l = 2*k, k a non negative integer + */ + private static final int ZZ_LEXSTATE[] = { + 0, 0, 1, 1 + }; + + /** + * Translates characters to character classes + */ + private static final String ZZ_CMAP_PACKED = + "\11\0\1\3\1\1\1\0\1\6\1\2\22\0\1\5\1\7\1\0"+ + "\1\7\26\0\1\10\2\0\1\10\36\0\1\4\uffa3\0"; + + /** + * Translates characters to character classes + */ + private static final char [] ZZ_CMAP = zzUnpackCMap(ZZ_CMAP_PACKED); + + /** + * Translates DFA states to action switch labels. + */ + private static final int [] ZZ_ACTION = zzUnpackAction(); + + private static final String ZZ_ACTION_PACKED_0 = + "\2\0\1\1\2\2\1\3\1\4\1\5\1\6\1\7"+ + "\1\10\1\4\1\10\1\0\1\1\2\5\1\0\1\7"; + + private static int [] zzUnpackAction() { + int [] result = new int[19]; + int offset = 0; + offset = zzUnpackAction(ZZ_ACTION_PACKED_0, offset, result); + return result; + } + + private static int zzUnpackAction(String packed, int offset, int [] result) { + int i = 0; /* index in packed string */ + int j = offset; /* index in unpacked array */ + int l = packed.length(); + while (i < l) { + int count = packed.charAt(i++); + int value = packed.charAt(i++); + do result[j++] = value; while (--count > 0); + } + return j; + } + + + /** + * Translates a state to a row index in the transition table + */ + private static final int [] ZZ_ROWMAP = zzUnpackRowMap(); + + private static final String ZZ_ROWMAP_PACKED_0 = + "\0\0\0\11\0\22\0\33\0\44\0\55\0\66\0\77"+ + "\0\33\0\110\0\121\0\132\0\143\0\66\0\154\0\165"+ + "\0\176\0\132\0\207"; + + private static int [] zzUnpackRowMap() { + int [] result = new int[19]; + int offset = 0; + offset = zzUnpackRowMap(ZZ_ROWMAP_PACKED_0, offset, result); + return result; + } + + private static int zzUnpackRowMap(String packed, int offset, int [] result) { + int i = 0; /* index in packed string */ + int j = offset; /* index in unpacked array */ + int l = packed.length(); + while (i < l) { + int high = packed.charAt(i++) << 16; + result[j++] = high | packed.charAt(i++); + } + return j; + } + + /** + * The transition table of the DFA + */ + private static final int [] ZZ_TRANS = zzUnpackTrans(); + + private static final String ZZ_TRANS_PACKED_0 = + "\1\3\1\4\1\5\1\6\1\7\2\6\1\10\1\11"+ + "\1\12\1\4\1\5\1\13\1\14\2\15\2\12\1\3"+ + "\3\0\1\16\2\0\1\3\13\0\1\4\12\0\1\6"+ + "\1\0\2\6\2\0\2\3\1\17\6\3\1\10\2\0"+ + "\1\20\1\21\2\20\1\10\1\20\1\12\2\0\1\12"+ + "\1\22\1\12\1\0\3\12\2\0\1\13\1\22\1\13"+ + "\1\15\4\12\1\23\6\12\3\0\1\15\1\0\2\15"+ + "\2\0\2\3\2\0\1\16\2\0\1\3\1\0\1\20"+ + "\2\0\6\20\1\10\1\3\1\17\6\10\2\12\1\0"+ + "\1\12\1\22\1\12\1\0\2\12"; + + private static int [] zzUnpackTrans() { + int [] result = new int[144]; + int offset = 0; + offset = zzUnpackTrans(ZZ_TRANS_PACKED_0, offset, result); + return result; + } + + private static int zzUnpackTrans(String packed, int offset, int [] result) { + int i = 0; /* index in packed string */ + int j = offset; /* index in unpacked array */ + int l = packed.length(); + while (i < l) { + int count = packed.charAt(i++); + int value = packed.charAt(i++); + value--; + do result[j++] = value; while (--count > 0); + } + return j; + } + + + /* error codes */ + private static final int ZZ_UNKNOWN_ERROR = 0; + private static final int ZZ_NO_MATCH = 1; + private static final int ZZ_PUSHBACK_2BIG = 2; + private static final char[] EMPTY_BUFFER = new char[0]; + private static final int YYEOF = -1; + private static java.io.Reader zzReader = null; // Fake + + /* error messages for the codes above */ + private static final String ZZ_ERROR_MSG[] = { + "Unkown internal scanner error", + "Error: could not match input", + "Error: pushback value was too large" + }; + + /** + * ZZ_ATTRIBUTE[aState] contains the attributes of state aState + */ + private static final int [] ZZ_ATTRIBUTE = zzUnpackAttribute(); + + private static final String ZZ_ATTRIBUTE_PACKED_0 = + "\2\0\1\1\1\11\4\1\1\11\4\1\1\0\3\1"+ + "\1\0\1\1"; + + private static int [] zzUnpackAttribute() { + int [] result = new int[19]; + int offset = 0; + offset = zzUnpackAttribute(ZZ_ATTRIBUTE_PACKED_0, offset, result); + return result; + } + + private static int zzUnpackAttribute(String packed, int offset, int [] result) { + int i = 0; /* index in packed string */ + int j = offset; /* index in unpacked array */ + int l = packed.length(); + while (i < l) { + int count = packed.charAt(i++); + int value = packed.charAt(i++); + do result[j++] = value; while (--count > 0); + } + return j; + } + + /** the current state of the DFA */ + private int zzState; + + /** the current lexical state */ + private int zzLexicalState = YYINITIAL; + + /** this buffer contains the current text to be matched and is + the source of the yytext() string */ + private CharSequence zzBuffer = ""; + + /** this buffer may contains the current text array to be matched when it is cheap to acquire it */ + private char[] zzBufferArray; + + /** the textposition at the last accepting state */ + private int zzMarkedPos; + + /** the textposition at the last state to be included in yytext */ + private int zzPushbackPos; + + /** the current text position in the buffer */ + private int zzCurrentPos; + + /** startRead marks the beginning of the yytext() string in the buffer */ + private int zzStartRead; + + /** endRead marks the last character in the buffer, that has been read + from input */ + private int zzEndRead; + + /** + * zzAtBOL == true <=> the scanner is currently at the beginning of a line + */ + private boolean zzAtBOL = true; + + /** zzAtEOF == true <=> the scanner is at the EOF */ + private boolean zzAtEOF; + + /** denotes if the user-EOF-code has already been executed */ + private boolean zzEOFDone; + + + SimpleLexer(java.io.Reader in) { + this.zzReader = in; + } + + /** + * Creates a new scanner. + * There is also java.io.Reader version of this constructor. + * + * @param in the java.io.Inputstream to read input from. + */ + SimpleLexer(java.io.InputStream in) { + this(new java.io.InputStreamReader(in)); + } + + /** + * Unpacks the compressed character translation table. + * + * @param packed the packed character translation table + * @return the unpacked character translation table + */ + private static char [] zzUnpackCMap(String packed) { + char [] map = new char[0x10000]; + int i = 0; /* index in packed string */ + int j = 0; /* index in unpacked array */ + while (i < 36) { + int count = packed.charAt(i++); + char value = packed.charAt(i++); + do map[j++] = value; while (--count > 0); + } + return map; + } + + public final int getTokenStart(){ + return zzStartRead; + } + + public final int getTokenEnd(){ + return getTokenStart() + yylength(); + } + + public void reset(CharSequence buffer, int start, int end,int initialState){ + zzBuffer = buffer; + zzBufferArray = com.intellij.util.text.CharArrayUtil.fromSequenceWithoutCopying(buffer); + zzCurrentPos = zzMarkedPos = zzStartRead = start; + zzPushbackPos = 0; + zzAtEOF = false; + zzAtBOL = true; + zzEndRead = end; + yybegin(initialState); + } + + /** + * Refills the input buffer. + * + * @return false, iff there was new input. + * + * @exception java.io.IOException if any I/O-Error occurs + */ + private boolean zzRefill() throws java.io.IOException { + return true; + } + + + /** + * Returns the current lexical state. + */ + public final int yystate() { + return zzLexicalState; + } + + + /** + * Enters a new lexical state + * + * @param newState the new lexical state + */ + public final void yybegin(int newState) { + zzLexicalState = newState; + } + + + /** + * Returns the text matched by the current regular expression. + */ + public final CharSequence yytext() { + return zzBuffer.subSequence(zzStartRead, zzMarkedPos); + } + + + /** + * Returns the character at position pos from the + * matched text. + * + * It is equivalent to yytext().charAt(pos), but faster + * + * @param pos the position of the character to fetch. + * A value from 0 to yylength()-1. + * + * @return the character at position pos + */ + public final char yycharat(int pos) { + return zzBufferArray != null ? zzBufferArray[zzStartRead+pos]:zzBuffer.charAt(zzStartRead+pos); + } + + + /** + * Returns the length of the matched text region. + */ + public final int yylength() { + return zzMarkedPos-zzStartRead; + } + + + /** + * Reports an error that occured while scanning. + * + * In a wellformed scanner (no or only correct usage of + * yypushback(int) and a match-all fallback rule) this method + * will only be called with things that "Can't Possibly Happen". + * If this method is called, something is seriously wrong + * (e.g. a JFlex bug producing a faulty scanner etc.). + * + * Usual syntax/scanner level error handling should be done + * in error fallback rules. + * + * @param errorCode the code of the errormessage to display + */ + private void zzScanError(int errorCode) { + String message; + try { + message = ZZ_ERROR_MSG[errorCode]; + } + catch (ArrayIndexOutOfBoundsException e) { + message = ZZ_ERROR_MSG[ZZ_UNKNOWN_ERROR]; + } + + throw new Error(message); + } + + + /** + * Pushes the specified amount of characters back into the input stream. + * + * They will be read again by then next call of the scanning method + * + * @param number the number of characters to be read again. + * This number must not be greater than yylength()! + */ + public void yypushback(int number) { + if ( number > yylength() ) + zzScanError(ZZ_PUSHBACK_2BIG); + + zzMarkedPos -= number; + } + + + /** + * Contains user EOF-code, which will be executed exactly once, + * when the end of file is reached + */ + private void zzDoEOF() { + if (!zzEOFDone) { + zzEOFDone = true; + + } + } + + + /** + * Resumes scanning until the next regular expression is matched, + * the end of input is encountered or an I/O-Error occurs. + * + * @return the next token + * @exception java.io.IOException if any I/O-Error occurs + */ + public IElementType advance() throws java.io.IOException { + int zzInput; + int zzAction; + + // cached fields: + int zzCurrentPosL; + int zzMarkedPosL; + int zzEndReadL = zzEndRead; + CharSequence zzBufferL = zzBuffer; + char[] zzBufferArrayL = zzBufferArray; + char [] zzCMapL = ZZ_CMAP; + + int [] zzTransL = ZZ_TRANS; + int [] zzRowMapL = ZZ_ROWMAP; + int [] zzAttrL = ZZ_ATTRIBUTE; + + while (true) { + zzMarkedPosL = zzMarkedPos; + + zzAction = -1; + + zzCurrentPosL = zzCurrentPos = zzStartRead = zzMarkedPosL; + + zzState = ZZ_LEXSTATE[zzLexicalState]; + + + zzForAction: { + while (true) { + + if (zzCurrentPosL < zzEndReadL) + zzInput = zzBufferL.charAt(zzCurrentPosL++); + else if (zzAtEOF) { + zzInput = YYEOF; + break zzForAction; + } + else { + // store back cached positions + zzCurrentPos = zzCurrentPosL; + zzMarkedPos = zzMarkedPosL; + boolean eof = zzRefill(); + // get translated positions and possibly new buffer + zzCurrentPosL = zzCurrentPos; + zzMarkedPosL = zzMarkedPos; + zzBufferL = zzBuffer; + zzEndReadL = zzEndRead; + if (eof) { + zzInput = YYEOF; + break zzForAction; + } + else { + zzInput = zzBufferL.charAt(zzCurrentPosL++); + } + } + int zzNext = zzTransL[ zzRowMapL[zzState] + zzCMapL[zzInput] ]; + if (zzNext == -1) break zzForAction; + zzState = zzNext; + + int zzAttributes = zzAttrL[zzState]; + if ( (zzAttributes & 1) == 1 ) { + zzAction = zzState; + zzMarkedPosL = zzCurrentPosL; + if ( (zzAttributes & 8) == 8 ) break zzForAction; + } + + } + } + + // store back cached position + zzMarkedPos = zzMarkedPosL; + + switch (zzAction < 0 ? zzAction : ZZ_ACTION[zzAction]) { + case 7: + { yybegin(YYINITIAL); return SimpleTypes.VALUE; + } + case 9: break; + case 6: + { yybegin(WAITING_VALUE); return SimpleTypes.SEPARATOR; + } + case 10: break; + case 5: + { yybegin(YYINITIAL); return SimpleTypes.COMMENT; + } + case 11: break; + case 4: + { return TokenType.BAD_CHARACTER; + } + case 12: break; + case 3: + { yybegin(YYINITIAL); return TokenType.WHITE_SPACE; + } + case 13: break; + case 8: + { yybegin(WAITING_VALUE); return TokenType.WHITE_SPACE; + } + case 14: break; + case 2: + { yybegin(YYINITIAL); return SimpleTypes.CRLF; + } + case 15: break; + case 1: + { yybegin(YYINITIAL); return SimpleTypes.KEY; + } + case 16: break; + default: + if (zzInput == YYEOF && zzStartRead == zzCurrentPos) { + zzAtEOF = true; + zzDoEOF(); + return null; + } + else { + zzScanError(ZZ_NO_MATCH); + } + } + } + } + + +} diff --git a/src/com/simpleplugin/SimpleLineMarkerProvider.java b/src/com/simpleplugin/SimpleLineMarkerProvider.java new file mode 100644 index 000000000..b803bf3ac --- /dev/null +++ b/src/com/simpleplugin/SimpleLineMarkerProvider.java @@ -0,0 +1,34 @@ +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 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 properties = SimpleUtil.findProperties(project, value.substring(7)); + if (properties.size() > 0) { + NavigationGutterIconBuilder builder = + NavigationGutterIconBuilder.create(SimpleIcons.FILE). + setTargets(properties). + setTooltipText("Navigate to a simple property"); + result.add(builder.createLineMarkerInfo(element)); + } + } + } + } +} \ No newline at end of file diff --git a/src/com/simpleplugin/SimpleParserDefinition.java b/src/com/simpleplugin/SimpleParserDefinition.java new file mode 100644 index 000000000..08a669e7f --- /dev/null +++ b/src/com/simpleplugin/SimpleParserDefinition.java @@ -0,0 +1,72 @@ +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.FlexAdapter; +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; + +import java.io.Reader; + +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.findInstance(SimpleLanguage.class)); + + @NotNull + @Override + public Lexer createLexer(Project project) { + return new FlexAdapter(new SimpleLexer((Reader) null)); + } + + @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); + } +} \ No newline at end of file diff --git a/src/com/simpleplugin/SimpleRefactoringSupportProvider.java b/src/com/simpleplugin/SimpleRefactoringSupportProvider.java new file mode 100644 index 000000000..8040801d7 --- /dev/null +++ b/src/com/simpleplugin/SimpleRefactoringSupportProvider.java @@ -0,0 +1,12 @@ +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; + } +} \ No newline at end of file diff --git a/src/com/simpleplugin/SimpleReference.java b/src/com/simpleplugin/SimpleReference.java new file mode 100644 index 000000000..cbb9b295d --- /dev/null +++ b/src/com/simpleplugin/SimpleReference.java @@ -0,0 +1,58 @@ +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 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 properties = SimpleUtil.findProperties(project, key); + List results = new ArrayList(); + 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 properties = SimpleUtil.findProperties(project); + List variants = new ArrayList(); + 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(); + } +} \ No newline at end of file diff --git a/src/com/simpleplugin/SimpleReferenceContributor.java b/src/com/simpleplugin/SimpleReferenceContributor.java new file mode 100644 index 000000000..8c61bf28e --- /dev/null +++ b/src/com/simpleplugin/SimpleReferenceContributor.java @@ -0,0 +1,26 @@ +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]; + } + }); + } +} \ No newline at end of file diff --git a/src/com/simpleplugin/SimpleStructureViewElement.java b/src/com/simpleplugin/SimpleStructureViewElement.java new file mode 100644 index 000000000..29ba22956 --- /dev/null +++ b/src/com/simpleplugin/SimpleStructureViewElement.java @@ -0,0 +1,72 @@ +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 treeElements = new ArrayList(properties.length); + for (SimpleProperty property : properties) { + treeElements.add(new SimpleStructureViewElement(property)); + } + return treeElements.toArray(new TreeElement[treeElements.size()]); + } else { + return EMPTY_ARRAY; + } + } +} \ No newline at end of file diff --git a/src/com/simpleplugin/SimpleStructureViewFactory.java b/src/com/simpleplugin/SimpleStructureViewFactory.java new file mode 100644 index 000000000..f1a616df6 --- /dev/null +++ b/src/com/simpleplugin/SimpleStructureViewFactory.java @@ -0,0 +1,23 @@ +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.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() { + return new SimpleStructureViewModel(psiFile); + } + }; + } +} \ No newline at end of file diff --git a/src/com/simpleplugin/SimpleStructureViewModel.java b/src/com/simpleplugin/SimpleStructureViewModel.java new file mode 100644 index 000000000..3d2318087 --- /dev/null +++ b/src/com/simpleplugin/SimpleStructureViewModel.java @@ -0,0 +1,32 @@ +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; + } +} \ No newline at end of file diff --git a/src/com/simpleplugin/SimpleSyntaxHighlighter.java b/src/com/simpleplugin/SimpleSyntaxHighlighter.java new file mode 100644 index 000000000..e5fafa471 --- /dev/null +++ b/src/com/simpleplugin/SimpleSyntaxHighlighter.java @@ -0,0 +1,58 @@ +package com.simpleplugin; + +import com.intellij.lexer.FlexAdapter; +import com.intellij.lexer.Lexer; +import com.intellij.openapi.editor.SyntaxHighlighterColors; +import com.intellij.openapi.editor.colors.TextAttributesKey; +import com.intellij.openapi.editor.markup.TextAttributes; +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 java.awt.*; +import java.io.Reader; + +import static com.intellij.openapi.editor.colors.TextAttributesKey.createTextAttributesKey; + +public class SimpleSyntaxHighlighter extends SyntaxHighlighterBase { + public static final TextAttributesKey SEPARATOR = createTextAttributesKey("SIMPLE_SEPARATOR", SyntaxHighlighterColors.OPERATION_SIGN); + public static final TextAttributesKey KEY = createTextAttributesKey("SIMPLE_KEY", SyntaxHighlighterColors.KEYWORD); + public static final TextAttributesKey VALUE = createTextAttributesKey("SIMPLE_VALUE", SyntaxHighlighterColors.STRING); + public static final TextAttributesKey COMMENT = createTextAttributesKey("SIMPLE_COMMENT", SyntaxHighlighterColors.LINE_COMMENT); + + static final TextAttributesKey BAD_CHARACTER = createTextAttributesKey("SIMPLE_BAD_CHARACTER", + new TextAttributes(Color.RED, null, null, null, Font.BOLD)); + + 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 FlexAdapter(new SimpleLexer((Reader) null)); + } + + @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; + } + } +} \ No newline at end of file diff --git a/src/com/simpleplugin/SimpleSyntaxHighlighterFactory.java b/src/com/simpleplugin/SimpleSyntaxHighlighterFactory.java new file mode 100644 index 000000000..e3a6fe428 --- /dev/null +++ b/src/com/simpleplugin/SimpleSyntaxHighlighterFactory.java @@ -0,0 +1,15 @@ +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(); + } +} \ No newline at end of file diff --git a/src/com/simpleplugin/SimpleTokenType.java b/src/com/simpleplugin/SimpleTokenType.java new file mode 100644 index 000000000..5723127b3 --- /dev/null +++ b/src/com/simpleplugin/SimpleTokenType.java @@ -0,0 +1,17 @@ +package com.simpleplugin; + +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(); + } +} \ No newline at end of file diff --git a/src/com/simpleplugin/SimpleUtil.java b/src/com/simpleplugin/SimpleUtil.java new file mode 100644 index 000000000..c2bb98218 --- /dev/null +++ b/src/com/simpleplugin/SimpleUtil.java @@ -0,0 +1,57 @@ +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 findProperties(Project project, String key) { + List result = null; + Collection 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(); + } + result.add(property); + } + } + } + } + } + return result != null ? result : Collections.emptyList(); + } + + public static List findProperties(Project project) { + List result = new ArrayList(); + Collection 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; + } +} \ No newline at end of file diff --git a/src/com/simpleplugin/icons/jar-gray.png b/src/com/simpleplugin/icons/jar-gray.png new file mode 100644 index 0000000000000000000000000000000000000000..4760e60a0314d426121957d2018c41b191c82474 GIT binary patch literal 729 zcmV;~0w(>5P)$LZLm>LXWxh1^NW~5I#a5z+0a|K?o$hv>*r~ znv(|)nN>8qSrgrLCo{7>v`|#-i)H?86eN|PkVcNpVKtOXf(q0^)(n{a2yBw`}@=|jOT!*r6mA> zVHil$6j2o2o}Qi}&vV?}-64u1xUP#*sRSX!jpun_j3LkS-+%=eWsJS8*X!T6wze9K zF=(wZolc1m0#ZtHc6Rn1zy|=w0G!W2^W}#3p4Z|>n5JGDW ztu>TV;^gF{cX)XC4ZsfoFlSZ(tZZ&>w!$#Hu`Elwu4`MCRT+&&gP8*W*x1-;9UL5V z=N)LR=Mf5p0tJu&0AsB3`1m-)i!n`;YOU$N0AzM37K?V4WyOBKU!F`RMZ++p@B5MC zI068))^jbHy_ zCbJIy?B?3PBE)fQbI$#Gy-qmikq`n|mVppL;y9+2O6C6%E-o&7#@MTJxm>Q*YF?h_ z=KA{jaO^gfV7^VQ2$@Qp!|H zAqWDHPNxITIfM{J09ajJjgF6xW6pUJ1i^4Roj!HD-RS7(C}NB~0e}#q$T>%+(}4+q z_If?H-EPalU=VL_Z+n!|8{4++X0sVfDT$Oao^{1JkCP;ky 1000) { + builder_.error("Maximum recursion level (" + 1000 + ") reached in " + funcName_); + return false; + } + return true; + } + + public static void empty_element_parsed_guard_(PsiBuilder builder_, int offset_, String funcName_) { + builder_.error("Empty element parsed in " + funcName_ +" at offset " + offset_); + } + + public static boolean invalid_left_marker_guard_(PsiBuilder builder_, PsiBuilder.Marker marker_, String funcName_) { + //builder_.error("Invalid left marker encountered in " + funcName_ +" at offset " + builder_.getCurrentOffset()); + boolean goodMarker = marker_ != null && ((LighterASTNode)marker_).getTokenType() != TokenType.ERROR_ELEMENT; + if (!goodMarker) return false; + ErrorState state = ErrorState.get(builder_); + + Frame frame = state.levelCheck.isEmpty() ? null : state.levelCheck.getLast(); + return frame == null || frame.errorReportedAt <= builder_.getCurrentOffset(); + } + + public static boolean consumeTokens(PsiBuilder builder_, int pin_, IElementType... tokens_) { + ErrorState state = ErrorState.get(builder_); + if (state.completionState != null && state.predicateSign) { + addCompletionVariant(state, state.completionState, builder_, tokens_, builder_.getCurrentOffset()); + } + // suppress single token completion + CompletionState completionState = state.completionState; + state.completionState = null; + boolean result_ = true; + boolean pinned_ = false; + for (int i = 0, tokensLength = tokens_.length; i < tokensLength; i++) { + if (pin_ > 0 && i == pin_) pinned_ = result_; + if ((result_ || pinned_) && !consumeToken(builder_, tokens_[i])) { + result_ = false; + if (pin_ < 0 || pinned_) report_error_(builder_); + } + } + state.completionState = completionState; + return pinned_ || result_; + } + + public static boolean consumeToken(PsiBuilder builder_, IElementType token) { + if (nextTokenIsInner(builder_, token, true)) { + builder_.advanceLexer(); + return true; + } + return false; + } + + public static boolean nextTokenIs(PsiBuilder builder_, IElementType token) { + return nextTokenIsInner(builder_, token, false); + } + + public static boolean nextTokenIsInner(PsiBuilder builder_, IElementType token, boolean force) { + ErrorState state = ErrorState.get(builder_); + if (state.completionState != null && !force) return true; + IElementType tokenType = builder_.getTokenType(); + if (!state.suppressErrors && state.predicateCount < 2) { + addVariant(state, builder_, token); + } + return token == tokenType; + } + + public static boolean replaceVariants(PsiBuilder builder_, int variantCount, String frameName) { + ErrorState state = ErrorState.get(builder_); + if (!state.suppressErrors && state.predicateCount < 2 && state.predicateSign) { + state.clearVariants(true, state.variants.size() - variantCount); + addVariantInner(state, builder_.getCurrentOffset(), frameName); + } + return true; + } + + public static void addVariant(PsiBuilder builder_, String text) { + addVariant(ErrorState.get(builder_), builder_, text); + } + + private static void addVariant(ErrorState state, PsiBuilder builder_, Object o) { + int offset = builder_.getCurrentOffset(); + addVariantInner(state, offset, o); + + CompletionState completionState = state.completionState; + if (completionState != null && state.predicateSign) { + addCompletionVariant(state, completionState, builder_, o, offset); + } + } + + private static void addVariantInner(ErrorState state, int offset, Object o) { + Variant variant = state.VARIANTS.alloc().init(offset, o); + if (state.predicateSign) { + state.variants.add(variant); + if (state.lastExpectedVariantOffset < variant.offset) { + state.lastExpectedVariantOffset = variant.offset; + } + } + else { + state.unexpected.add(variant); + } + } + + public static boolean consumeToken(PsiBuilder builder_, String text) { + ErrorState state = ErrorState.get(builder_); + if (!state.suppressErrors && state.predicateCount < 2) { + addVariant(state, builder_, text); + } + return consumeTokenInner(builder_, text, state.caseSensitive); + } + + public static boolean consumeTokenInner(PsiBuilder builder_, String text, boolean caseSensitive) { + final CharSequence sequence = builder_.getOriginalText(); + final int offset = builder_.getCurrentOffset(); + final int endOffset = offset + text.length(); + CharSequence tokenText = sequence.subSequence(offset, Math.min(endOffset, sequence.length())); + + if (Comparing.equal(text, tokenText, caseSensitive)) { + int count = 0; + while (true) { + final int nextOffset = builder_.rawTokenTypeStart(++ count); + if (nextOffset > endOffset) { + return false; + } + else if (nextOffset == endOffset) { + break; + } + } + while (count-- > 0) builder_.advanceLexer(); + return true; + } + return false; + } + + private static void addCompletionVariant(ErrorState state, + CompletionState completionState, + PsiBuilder builder_, + Object o, + int offset) { + boolean add = false; + int diff = completionState.offset - offset; + String text = completionState.convertItem(o); + int length = text == null? 0 : text.length(); + if (length == 0) return; + if (diff == 0) { + add = true; + } + else if (diff > 0 && diff <= length) { + CharSequence fragment = builder_.getOriginalText().subSequence(offset, completionState.offset); + add = StringUtil.startsWithIgnoreCase(text, fragment.toString()); + } + else if (diff < 0) { + for (int i=-1; ; i--) { + IElementType type = builder_.rawLookup(i); + int tokenStart = builder_.rawTokenTypeStart(i); + if (state.whitespaceTokens.contains(type) || state.commentTokens.contains(type)) { + diff = completionState.offset - tokenStart; + } + else if (type != null && tokenStart < completionState.offset) { + CharSequence fragment = builder_.getOriginalText().subSequence(tokenStart, completionState.offset); + if (StringUtil.startsWithIgnoreCase(text, fragment.toString())) { + diff = completionState.offset - tokenStart; + } + break; + } + else break; + } + add = diff >= 0 && diff < length; + } + add = add && length > 1 && !(text.charAt(0) == '<' && text.charAt(length - 1) == '>') && + !(text.charAt(0) == '\'' && text.charAt(length - 1) == '\'' && length < 5); + if (add) { + completionState.items.add(text); + } + } + + + public static final String _SECTION_NOT_ = "_SECTION_NOT_"; + public static final String _SECTION_AND_ = "_SECTION_AND_"; + public static final String _SECTION_RECOVER_ = "_SECTION_RECOVER_"; + public static final String _SECTION_GENERAL_ = "_SECTION_GENERAL_"; + + public static void enterErrorRecordingSection(PsiBuilder builder_, int level, @NotNull String sectionType, @Nullable String frameName) { + ErrorState state = ErrorState.get(builder_); + Frame frame = state.FRAMES.alloc().init(builder_.getCurrentOffset(), level, sectionType, frameName, state.variants.size()); + state.levelCheck.add(frame); + if (sectionType == _SECTION_AND_) { + if (state.predicateCount == 0 && !state.predicateSign) { + throw new AssertionError("Incorrect false predicate sign"); + } + state.predicateCount++; + } + else if (sectionType == _SECTION_NOT_) { + if (state.predicateCount == 0) { + state.predicateSign = false; + } + else { + state.predicateSign = !state.predicateSign; + } + state.predicateCount++; + } + } + + public static boolean exitErrorRecordingSection(PsiBuilder builder_, + int level, + boolean result, + boolean pinned, + @NotNull String sectionType, + @Nullable Parser eatMore) { + ErrorState state = ErrorState.get(builder_); + + Frame frame = state.levelCheck.pollLast(); + int initialOffset = builder_.getCurrentOffset(); + if (frame == null || level != frame.level || !sectionType.equals(frame.section)) { + LOG.error("Unbalanced error section: got " + new Frame().init(initialOffset, level, sectionType, "", 0) + ", expected " + frame); + if (frame != null) state.FRAMES.recycle(frame); + return result; + } + if (sectionType == _SECTION_AND_ || sectionType == _SECTION_NOT_) { + state.predicateCount--; + if (sectionType == _SECTION_NOT_) state.predicateSign = !state.predicateSign; + state.FRAMES.recycle(frame); + return result; + } + if (!result && !pinned && initialOffset == frame.offset && state.lastExpectedVariantOffset == frame.offset && + frame.name != null && state.variants.size() - frame.variantCount > 1) { + state.clearVariants(true, frame.variantCount); + addVariantInner(state, initialOffset, frame.name); + } + if (sectionType == _SECTION_RECOVER_ && !state.suppressErrors && eatMore != null) { + state.suppressErrors = true; + final boolean eatMoreFlagOnce = !builder_.eof() && eatMore.parse(builder_, frame.level + 1); + final int lastErrorPos = getLastVariantOffset(state, initialOffset); + boolean eatMoreFlag = eatMoreFlagOnce || frame.offset == initialOffset && lastErrorPos > frame.offset; + + final LighterASTNode latestDoneMarker = + (pinned || result) && (state.altMode || lastErrorPos > initialOffset) && + eatMoreFlagOnce ? builder_.getLatestDoneMarker() : null; + PsiBuilder.Marker extensionMarker = null; + IElementType extensionTokenType = null; + if (latestDoneMarker instanceof PsiBuilder.Marker) { + extensionMarker = ((PsiBuilder.Marker)latestDoneMarker).precede(); + extensionTokenType = latestDoneMarker.getTokenType(); + ((PsiBuilder.Marker)latestDoneMarker).drop(); + } + // advance to the last error pos + // skip tokens until lastErrorPos. parseAsTree might look better here... + int parenCount = 0; + while (eatMoreFlag && builder_.getCurrentOffset() < lastErrorPos) { + if (state.braces != null) { + if (builder_.getTokenType() == state.braces[0].getLeftBraceType()) parenCount ++; + else if (builder_.getTokenType() == state.braces[0].getRightBraceType()) parenCount --; + } + builder_.advanceLexer(); + eatMoreFlag = parenCount != 0 || eatMore.parse(builder_, frame.level + 1); + } + boolean errorReported = frame.errorReportedAt == initialOffset; + if (errorReported) { + if (eatMoreFlag) { + builder_.advanceLexer(); + parseAsTree(state, builder_, frame.level + 1, DUMMY_BLOCK, true, TOKEN_ADVANCER, eatMore); + } + } + else if (eatMoreFlag) { + String tokenText = builder_.getTokenText(); + String expectedText = state.getExpectedText(builder_); + PsiBuilder.Marker mark = builder_.mark(); + builder_.advanceLexer(); + final String gotText = !expectedText.isEmpty() ? "got '" + tokenText + "'" : "'" + tokenText + "' unexpected"; + mark.error(expectedText + gotText); + parseAsTree(state, builder_, frame.level + 1, DUMMY_BLOCK, true, TOKEN_ADVANCER, eatMore); + errorReported = true; + } + else if (eatMoreFlagOnce || (!result && frame.offset != builder_.getCurrentOffset())) { + reportError(state, builder_, true); + errorReported = true; + } + if (extensionMarker != null) { + extensionMarker.done(extensionTokenType); + } + state.suppressErrors = false; + if (errorReported || result) { + state.clearVariants(true, 0); + state.clearVariants(false, 0); + state.lastExpectedVariantOffset = -1; + } + if (!result && eatMoreFlagOnce && frame.offset != builder_.getCurrentOffset()) result = true; + } + else if (!result && pinned && frame.errorReportedAt < 0) { + // do not report if there're errors after current offset + if (getLastVariantOffset(state, initialOffset) == initialOffset) { + // do not force, inner recoverRoot might have skipped some tokens + if (reportError(state, builder_, false)) { + frame.errorReportedAt = initialOffset; + } + } + } + // propagate errorReportedAt up the stack to avoid duplicate reporting + Frame prevFrame = state.levelCheck.isEmpty() ? null : state.levelCheck.getLast(); + if (prevFrame != null && prevFrame.errorReportedAt < frame.errorReportedAt) prevFrame.errorReportedAt = frame.errorReportedAt; + state.FRAMES.recycle(frame); + return result; + } + + public static boolean report_error_(PsiBuilder builder_, boolean current_) { + if (!current_) report_error_(builder_); + return current_; + } + + public static void report_error_(PsiBuilder builder_) { + ErrorState state = ErrorState.get(builder_); + + Frame frame = state.levelCheck.isEmpty()? null : state.levelCheck.getLast(); + if (frame == null) { + LOG.error("Unbalanced error section: got null , expected " + frame); + return; + } + int offset = builder_.getCurrentOffset(); + if (frame.errorReportedAt < offset && getLastVariantOffset(state, builder_.getCurrentOffset()) <= offset) { + if (reportError(state, builder_, true)) { + frame.errorReportedAt = offset; + } + } + } + + private static int getLastVariantOffset(ErrorState state, int defValue) { + return state.lastExpectedVariantOffset < 0? defValue : state.lastExpectedVariantOffset; + } + + private static boolean reportError(ErrorState state, PsiBuilder builder_, boolean force) { + String expectedText = state.getExpectedText(builder_); + boolean notEmpty = StringUtil.isNotEmpty(expectedText); + if (force || notEmpty) { + final String gotText = builder_.eof()? "unexpected end of file" : + notEmpty? "got '" + builder_.getTokenText() +"'" : + "'" + builder_.getTokenText() +"' unexpected"; + builder_.error(expectedText + gotText); + return true; + } + return false; + } + + + public static final Key COMPLETION_STATE_KEY = Key.create("COMPLETION_STATE_KEY"); + + public static class CompletionState implements Function { + public final int offset; + public final Collection items = new THashSet(); + + public CompletionState(int offset) { + this.offset = offset; + } + + @Nullable + public String convertItem(Object o) { + return o instanceof Object[] ? StringUtil.join((Object[]) o, this, " ") : o.toString(); + } + + @Override + public String fun(Object o) { + return o.toString(); + } + } + + public static class Builder extends PsiBuilderAdapter { + final ErrorState state; + final PsiParser parser; + + public Builder(PsiBuilder builder, ErrorState state, PsiParser parser) { + super(builder); + this.state = state; + this.parser = parser; + } + + public Lexer getLexer() { + return ((PsiBuilderImpl)myDelegate).getLexer(); + } + } + + public static PsiBuilder adapt_builder_(IElementType root, PsiBuilder builder, PsiParser parser) { + ErrorState state = new ErrorState(); + ErrorState.initState(root, builder, state); + return new Builder(builder, state, parser); + } + + public static class ErrorState { + int predicateCount; + boolean predicateSign = true; + boolean suppressErrors; + final LinkedList levelCheck = new LinkedList(); + CompletionState completionState; + + private boolean caseSensitive; + private TokenSet whitespaceTokens = TokenSet.EMPTY; + private TokenSet commentTokens = TokenSet.EMPTY; + public BracePair[] braces; + public boolean altMode; + + private int lastExpectedVariantOffset = -1; + ArrayList variants = new ArrayList(); + ArrayList unexpected = new ArrayList(); + final LimitedPool VARIANTS = new LimitedPool(5000, new LimitedPool.ObjectFactory() { + public Variant create() { + return new Variant(); + } + + public void cleanup(final Variant o) { + } + }); + final LimitedPool FRAMES = new LimitedPool(100, new LimitedPool.ObjectFactory() { + public Frame create() { + return new Frame(); + } + + public void cleanup(final Frame o) { + } + }); + + public static ErrorState get(PsiBuilder builder) { + return ((Builder)builder).state; + } + + private static void initState(IElementType root, PsiBuilder builder, ErrorState state) { + PsiFile file = builder.getUserDataUnprotected(FileContextUtil.CONTAINING_FILE_KEY); + state.completionState = file == null? null: file.getUserData(COMPLETION_STATE_KEY); + Language language = file == null? root.getLanguage() : file.getLanguage(); + state.caseSensitive = language.isCaseSensitive(); + ParserDefinition parserDefinition = LanguageParserDefinitions.INSTANCE.forLanguage(language); + if (parserDefinition != null) { + state.commentTokens = parserDefinition.getCommentTokens(); + state.whitespaceTokens = parserDefinition.getWhitespaceTokens(); + } + PairedBraceMatcher matcher = LanguageBraceMatching.INSTANCE.forLanguage(language); + state.braces = matcher == null ? null : matcher.getPairs(); + if (state.braces != null && state.braces.length == 0) state.braces = null; + } + + public String getExpectedText(PsiBuilder builder_) { + int offset = builder_.getCurrentOffset(); + StringBuilder sb = new StringBuilder(); + if (addExpected(sb, offset, true)) { + sb.append(" expected, "); + } + else if (addExpected(sb, offset, false)) sb.append(" unexpected, "); + return sb.toString(); + } + + private static final int MAX_VARIANTS_TO_DISPLAY = Integer.MAX_VALUE; + private boolean addExpected(StringBuilder sb, int offset, boolean expected) { + String[] strings = new String[variants.size()]; + long[] hashes = new long[strings.length]; + Arrays.fill(strings, ""); + int count = 0; + loop: for (Variant variant : expected? variants : unexpected) { + if (offset == variant.offset) { + String text = variant.object.toString(); + long hash = StringHash.calc(text); + for (int i=0; i 0) { + if (count > MAX_VARIANTS_TO_DISPLAY) { + sb.append(" and ..."); + break; + } + else { + sb.append(", "); + } + } + char c = s.charAt(0); + String displayText = c == '<' || StringUtil.isJavaIdentifierStart(c) ? s : '\'' + s + '\''; + sb.append(displayText); + } + if (count > 1 && count < MAX_VARIANTS_TO_DISPLAY) { + int idx = sb.lastIndexOf(", "); + sb.replace(idx, idx + 1, " or"); + } + return count > 0; + } + + void clearVariants(boolean expected, int start) { + ArrayList list = expected? variants : unexpected; + for (int i = start, len = list.size(); i < len; i ++) { + VARIANTS.recycle(list.get(i)); + } + list.subList(start, list.size()).clear(); + } + } + + public static class Frame { + int offset; + int level; + String section; + String name; + int variantCount; + int errorReportedAt; + + public Frame() { + } + + public Frame init(int offset, int level, String section, String name, int variantCount) { + this.offset = offset; + this.level = level; + this.section = section; + this.name = name; + this.variantCount = variantCount; + this.errorReportedAt = -1; + return this; + } + + @Override + public String toString() { + return "<"+offset+", "+section+", "+level+">"; + } + } + + + public static class Variant { + int offset; + Object object; + + public Variant init(int offset, Object text) { + this.offset = offset; + this.object = text; + return this; + } + + @Override + public String toString() { + return "<" + offset + ", " + object + ">"; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + Variant variant = (Variant)o; + + if (offset != variant.offset) return false; + if (!this.object.equals(variant.object)) return false; + + return true; + } + + @Override + public int hashCode() { + int result = offset; + result = 31 * result + object.hashCode(); + return result; + } + } + + @Nullable + private static IElementType getClosingBracket(ErrorState state, IElementType type) { + if (state.braces == null) return null; + for (BracePair pair : state.braces) { + if (type == pair.getLeftBraceType()) return pair.getRightBraceType(); + } + return null; + } + + + private static final int MAX_CHILDREN_IN_TREE = 10; + public static boolean parseAsTree(ErrorState state, final PsiBuilder builder_, int level, final IElementType chunkType, + boolean checkBraces, final Parser parser, final Parser eatMoreCondition) { + final LinkedList> parenList = new LinkedList>(); + final LinkedList> siblingList = new LinkedList>(); + PsiBuilder.Marker marker = null; + + final Runnable checkSiblingsRunnable = new Runnable() { + public void run() { + main: + while (!siblingList.isEmpty()) { + final Pair parenPair = parenList.peek(); + final int rating = siblingList.getFirst().second; + int count = 0; + for (Pair pair : siblingList) { + if (pair.second != rating || parenPair != null && pair.first == parenPair.second) break main; + if (++count >= MAX_CHILDREN_IN_TREE) { + final PsiBuilder.Marker parentMarker = pair.first.precede(); + while (count-- > 0) { + siblingList.removeFirst(); + } + parentMarker.done(chunkType); + siblingList.addFirst(Pair.create(parentMarker, rating + 1)); + continue main; + } + } + break; + } + } + }; + boolean checkParens = state.braces != null && checkBraces; + int totalCount = 0; + int tokenCount = 0; + if (checkParens && builder_.rawLookup(-1) == state.braces[0].getLeftBraceType()) { + LighterASTNode doneMarker = builder_.getLatestDoneMarker(); + if (doneMarker != null && doneMarker.getStartOffset() == builder_.rawTokenTypeStart(-1) && doneMarker.getTokenType() == TokenType.ERROR_ELEMENT) { + parenList.add(Pair.create(((PsiBuilder.Marker)doneMarker).precede(), (PsiBuilder.Marker)null)); + } + } + while (true) { + final IElementType tokenType = builder_.getTokenType(); + if (checkParens && (tokenType == state.braces[0].getLeftBraceType() || tokenType == state.braces[0].getRightBraceType() && !parenList.isEmpty())) { + if (marker != null) { + marker.done(chunkType); + siblingList.addFirst(Pair.create(marker, 1)); + marker = null; + tokenCount = 0; + } + if (tokenType == state.braces[0].getLeftBraceType()) { + final Pair prev = siblingList.peek(); + parenList.addFirst(Pair.create(builder_.mark(), prev == null ? null : prev.first)); + } + checkSiblingsRunnable.run(); + builder_.advanceLexer(); + if (tokenType == state.braces[0].getRightBraceType()) { + final Pair pair = parenList.removeFirst(); + pair.first.done(chunkType); + // drop all markers inside parens + while (!siblingList.isEmpty() && siblingList.getFirst().first != pair.second) { + siblingList.removeFirst(); + } + siblingList.addFirst(Pair.create(pair.first, 1)); + checkSiblingsRunnable.run(); + } + } + else { + if (marker == null) { + marker = builder_.mark(); + } + final boolean result = (state.altMode && !parenList.isEmpty() || eatMoreCondition.parse(builder_, level + 1)) && parser.parse(builder_, level + 1); + if (result) { + tokenCount++; + totalCount++; + } + if (!result) { + break; + } + } + + if (tokenCount >= MAX_CHILDREN_IN_TREE && marker != null) { + marker.done(chunkType); + siblingList.addFirst(Pair.create(marker, 1)); + checkSiblingsRunnable.run(); + marker = null; + tokenCount = 0; + } + } + if (marker != null) { + marker.drop(); + } + for (Pair pair : parenList) { + pair.first.drop(); + } + return totalCount != 0; + } + + private static class DummyBlockElementType extends IElementType implements ICompositeElementType{ + DummyBlockElementType() { + super("DUMMY_BLOCK", Language.ANY); + } + + @NotNull + @Override + public ASTNode createCompositeNode() { + return new DummyBlock(); + } + } + + public static class DummyBlock extends CompositePsiElement { + DummyBlock() { + super(DUMMY_BLOCK); + } + + @Override + public PsiReference[] getReferences() { + return PsiReference.EMPTY_ARRAY; + } + + @Override + public boolean canNavigateToSource() { + return false; + } + + @Override + public boolean canNavigate() { + return false; + } + + @NotNull + @Override + public Language getLanguage() { + return getParent().getLanguage(); + } + } +} \ No newline at end of file diff --git a/src/com/simpleplugin/psi/SimpleElementFactory.java b/src/com/simpleplugin/psi/SimpleElementFactory.java new file mode 100644 index 000000000..704e636b3 --- /dev/null +++ b/src/com/simpleplugin/psi/SimpleElementFactory.java @@ -0,0 +1,18 @@ +package com.simpleplugin.psi; + +import com.intellij.openapi.project.Project; +import com.intellij.psi.PsiFileFactory; +import com.simpleplugin.SimpleFileType; + +public class SimpleElementFactory { + public static SimpleProperty createProperty(Project project, String name) { + final SimpleFile file = createFile(project, name); + return (SimpleProperty) file.getFirstChild(); + } + + public static SimpleFile createFile(Project project, String text) { + String name = "dummy.simple"; + return (SimpleFile) PsiFileFactory.getInstance(project). + createFileFromText(name, SimpleFileType.INSTANCE, text); + } +} \ No newline at end of file diff --git a/src/com/simpleplugin/psi/SimpleElementType.java b/src/com/simpleplugin/psi/SimpleElementType.java new file mode 100644 index 000000000..34e5c3464 --- /dev/null +++ b/src/com/simpleplugin/psi/SimpleElementType.java @@ -0,0 +1,12 @@ +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); + } +} \ No newline at end of file diff --git a/src/com/simpleplugin/psi/SimpleFile.java b/src/com/simpleplugin/psi/SimpleFile.java new file mode 100644 index 000000000..21c8adc3b --- /dev/null +++ b/src/com/simpleplugin/psi/SimpleFile.java @@ -0,0 +1,32 @@ +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); + } +} \ No newline at end of file diff --git a/src/com/simpleplugin/psi/SimpleNamedElement.java b/src/com/simpleplugin/psi/SimpleNamedElement.java new file mode 100644 index 000000000..8c4d9260c --- /dev/null +++ b/src/com/simpleplugin/psi/SimpleNamedElement.java @@ -0,0 +1,6 @@ +package com.simpleplugin.psi; + +import com.intellij.psi.PsiNameIdentifierOwner; + +public interface SimpleNamedElement extends PsiNameIdentifierOwner { +} \ No newline at end of file diff --git a/src/com/simpleplugin/psi/SimpleTokenType.java b/src/com/simpleplugin/psi/SimpleTokenType.java new file mode 100644 index 000000000..6bdb585b4 --- /dev/null +++ b/src/com/simpleplugin/psi/SimpleTokenType.java @@ -0,0 +1,17 @@ +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(); + } +} \ No newline at end of file diff --git a/src/com/simpleplugin/psi/impl/SimpleNamedElementImpl.java b/src/com/simpleplugin/psi/impl/SimpleNamedElementImpl.java new file mode 100644 index 000000000..dcb333a62 --- /dev/null +++ b/src/com/simpleplugin/psi/impl/SimpleNamedElementImpl.java @@ -0,0 +1,12 @@ +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); + } +} \ No newline at end of file diff --git a/src/com/simpleplugin/psi/impl/SimplePsiImplUtil.java b/src/com/simpleplugin/psi/impl/SimplePsiImplUtil.java new file mode 100644 index 000000000..f772430e5 --- /dev/null +++ b/src/com/simpleplugin/psi/impl/SimplePsiImplUtil.java @@ -0,0 +1,78 @@ +package com.simpleplugin.psi.impl; + +import com.intellij.lang.ASTNode; +import com.intellij.navigation.ItemPresentation; +import com.intellij.psi.PsiElement; +import com.simpleplugin.SimpleIcons; +import com.simpleplugin.psi.SimpleElementFactory; +import com.simpleplugin.psi.SimpleProperty; +import com.simpleplugin.psi.SimpleTypes; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; + +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; + } + } + + public static String getName(SimpleProperty element) { + return getKey(element); + } + + public static PsiElement setName(SimpleProperty element, String newName) { + ASTNode keyNode = element.getNode().findChildByType(SimpleTypes.KEY); + if (keyNode != null) { + + SimpleProperty property = SimpleElementFactory.createProperty(element.getProject(), newName); + ASTNode newKeyNode = property.getFirstChild().getNode(); + element.getNode().replaceChild(keyNode, newKeyNode); + } + return element; + } + + public static PsiElement getNameIdentifier(SimpleProperty element) { + ASTNode keyNode = element.getNode().findChildByType(SimpleTypes.KEY); + if (keyNode != null) { + return keyNode.getPsi(); + } else { + return null; + } + } + + public static ItemPresentation getPresentation(final SimpleProperty element) { + return new ItemPresentation() { + @Nullable + @Override + public String getPresentableText() { + return element.getKey(); + } + + @Nullable + @Override + public String getLocationString() { + return element.getContainingFile().getName(); + } + + @Nullable + @Override + public Icon getIcon(boolean unused) { + return SimpleIcons.FILE; + } + }; + } +} \ No newline at end of file