diff --git a/simple_plugin/.idea/.name b/simple_plugin/.idea/.name new file mode 100644 index 000000000..316279c80 --- /dev/null +++ b/simple_plugin/.idea/.name @@ -0,0 +1 @@ +SimplePlugin \ No newline at end of file diff --git a/simple_plugin/.idea/ant.xml b/simple_plugin/.idea/ant.xml new file mode 100644 index 000000000..2581ca3fe --- /dev/null +++ b/simple_plugin/.idea/ant.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/simple_plugin/.idea/compiler.xml b/simple_plugin/.idea/compiler.xml new file mode 100644 index 000000000..217af471a --- /dev/null +++ b/simple_plugin/.idea/compiler.xml @@ -0,0 +1,23 @@ + + + + + + diff --git a/simple_plugin/.idea/copyright/profiles_settings.xml b/simple_plugin/.idea/copyright/profiles_settings.xml new file mode 100644 index 000000000..e7bedf337 --- /dev/null +++ b/simple_plugin/.idea/copyright/profiles_settings.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/simple_plugin/.idea/encodings.xml b/simple_plugin/.idea/encodings.xml new file mode 100644 index 000000000..e206d70d8 --- /dev/null +++ b/simple_plugin/.idea/encodings.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/simple_plugin/.idea/misc.xml b/simple_plugin/.idea/misc.xml new file mode 100644 index 000000000..1035ff89a --- /dev/null +++ b/simple_plugin/.idea/misc.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/simple_plugin/.idea/modules.xml b/simple_plugin/.idea/modules.xml new file mode 100644 index 000000000..c6008778a --- /dev/null +++ b/simple_plugin/.idea/modules.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/simple_plugin/.idea/runConfigurations/Plugin.xml b/simple_plugin/.idea/runConfigurations/Plugin.xml new file mode 100644 index 000000000..da96a36dd --- /dev/null +++ b/simple_plugin/.idea/runConfigurations/Plugin.xml @@ -0,0 +1,17 @@ + + + + + \ No newline at end of file diff --git a/simple_plugin/.idea/runConfigurations/Tests.xml b/simple_plugin/.idea/runConfigurations/Tests.xml new file mode 100644 index 000000000..78f9481d0 --- /dev/null +++ b/simple_plugin/.idea/runConfigurations/Tests.xml @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/simple_plugin/.idea/scopes/scope_settings.xml b/simple_plugin/.idea/scopes/scope_settings.xml new file mode 100644 index 000000000..922003b84 --- /dev/null +++ b/simple_plugin/.idea/scopes/scope_settings.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/simple_plugin/.idea/vcs.xml b/simple_plugin/.idea/vcs.xml new file mode 100644 index 000000000..275077f82 --- /dev/null +++ b/simple_plugin/.idea/vcs.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/simple_plugin/JFlex.jar b/simple_plugin/JFlex.jar new file mode 100644 index 000000000..8bef2fdba Binary files /dev/null and b/simple_plugin/JFlex.jar differ diff --git a/simple_plugin/META-INF/plugin.xml b/simple_plugin/META-INF/plugin.xml new file mode 100644 index 000000000..601bc4d54 --- /dev/null +++ b/simple_plugin/META-INF/plugin.xml @@ -0,0 +1,60 @@ + + com.simpleplugin.unique.plugin.id + Simple + 1.0 + YourCompany + + + most HTML tags may be used + ]]> + + + most HTML tags may be used + ]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/simple_plugin/SimplePlugin.iml b/simple_plugin/SimplePlugin.iml new file mode 100644 index 000000000..7bc787656 --- /dev/null +++ b/simple_plugin/SimplePlugin.iml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/simple_plugin/gen/com/simpleplugin/parser/SimpleParser.java b/simple_plugin/gen/com/simpleplugin/parser/SimpleParser.java new file mode 100644 index 000000000..3e28b7da7 --- /dev/null +++ b/simple_plugin/gen/com/simpleplugin/parser/SimpleParser.java @@ -0,0 +1,104 @@ +// This is a generated file. Not intended for manual editing. +package com.simpleplugin.parser; + +import com.intellij.lang.PsiBuilder; +import com.intellij.lang.PsiBuilder.Marker; +import static com.simpleplugin.psi.SimpleTypes.*; +import static com.intellij.lang.parser.GeneratedParserUtilBase.*; +import com.intellij.psi.tree.IElementType; +import com.intellij.lang.ASTNode; +import com.intellij.psi.tree.TokenSet; +import com.intellij.lang.PsiParser; +import com.intellij.lang.LightPsiParser; + +@SuppressWarnings({"SimplifiableIfStatement", "UnusedAssignment"}) +public class SimpleParser implements PsiParser, LightPsiParser { + + public ASTNode parse(IElementType t, PsiBuilder b) { + parseLight(t, b); + return b.getTreeBuilt(); + } + + public void parseLight(IElementType t, PsiBuilder b) { + boolean r; + b = adapt_builder_(t, b, this, null); + Marker m = enter_section_(b, 0, _COLLAPSE_, null); + if (t == PROPERTY) { + r = property(b, 0); + } + else { + r = parse_root_(t, b, 0); + } + exit_section_(b, 0, m, t, r, true, TRUE_CONDITION); + } + + protected boolean parse_root_(IElementType t, PsiBuilder b, int l) { + return simpleFile(b, l + 1); + } + + /* ********************************************************** */ + // property|COMMENT|CRLF + static boolean item_(PsiBuilder b, int l) { + if (!recursion_guard_(b, l, "item_")) return false; + boolean r; + Marker m = enter_section_(b); + r = property(b, l + 1); + if (!r) r = consumeToken(b, COMMENT); + if (!r) r = consumeToken(b, CRLF); + exit_section_(b, m, null, r); + return r; + } + + /* ********************************************************** */ + // (KEY? SEPARATOR VALUE?) | KEY + public static boolean property(PsiBuilder b, int l) { + if (!recursion_guard_(b, l, "property")) return false; + if (!nextTokenIs(b, "", KEY, SEPARATOR)) return false; + boolean r; + Marker m = enter_section_(b, l, _NONE_, ""); + r = property_0(b, l + 1); + if (!r) r = consumeToken(b, KEY); + exit_section_(b, l, m, PROPERTY, r, false, null); + return r; + } + + // KEY? SEPARATOR VALUE? + private static boolean property_0(PsiBuilder b, int l) { + if (!recursion_guard_(b, l, "property_0")) return false; + boolean r; + Marker m = enter_section_(b); + r = property_0_0(b, l + 1); + r = r && consumeToken(b, SEPARATOR); + r = r && property_0_2(b, l + 1); + exit_section_(b, m, null, r); + return r; + } + + // KEY? + private static boolean property_0_0(PsiBuilder b, int l) { + if (!recursion_guard_(b, l, "property_0_0")) return false; + consumeToken(b, KEY); + return true; + } + + // VALUE? + private static boolean property_0_2(PsiBuilder b, int l) { + if (!recursion_guard_(b, l, "property_0_2")) return false; + consumeToken(b, VALUE); + return true; + } + + /* ********************************************************** */ + // item_* + static boolean simpleFile(PsiBuilder b, int l) { + if (!recursion_guard_(b, l, "simpleFile")) return false; + int c = current_position_(b); + while (true) { + if (!item_(b, l + 1)) break; + if (!empty_element_parsed_guard_(b, "simpleFile", c)) break; + c = current_position_(b); + } + return true; + } + +} diff --git a/simple_plugin/gen/com/simpleplugin/psi/SimpleProperty.java b/simple_plugin/gen/com/simpleplugin/psi/SimpleProperty.java new file mode 100644 index 000000000..2ff5df042 --- /dev/null +++ b/simple_plugin/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/simple_plugin/gen/com/simpleplugin/psi/SimpleTypes.java b/simple_plugin/gen/com/simpleplugin/psi/SimpleTypes.java new file mode 100644 index 000000000..36792334c --- /dev/null +++ b/simple_plugin/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/simple_plugin/gen/com/simpleplugin/psi/SimpleVisitor.java b/simple_plugin/gen/com/simpleplugin/psi/SimpleVisitor.java new file mode 100644 index 000000000..504829968 --- /dev/null +++ b/simple_plugin/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/simple_plugin/gen/com/simpleplugin/psi/impl/SimplePropertyImpl.java b/simple_plugin/gen/com/simpleplugin/psi/impl/SimplePropertyImpl.java new file mode 100644 index 000000000..7c0abca71 --- /dev/null +++ b/simple_plugin/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/simple_plugin/idea-flex.skeleton b/simple_plugin/idea-flex.skeleton new file mode 100644 index 000000000..234a62c41 --- /dev/null +++ b/simple_plugin/idea-flex.skeleton @@ -0,0 +1,251 @@ + /** initial size of the lookahead buffer */ +--- private static final int ZZ_BUFFERSIZE = ...; + + /** lexical states */ +--- lexical states, charmap + + /* 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" + }; + +--- isFinal list + /** 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; + +--- user class code + +--- constructor declaration + + 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 + */ +--- zzScanError declaration + String message; + try { + message = ZZ_ERROR_MSG[errorCode]; + } + catch (ArrayIndexOutOfBoundsException e) { + message = ZZ_ERROR_MSG[ZZ_UNKNOWN_ERROR]; + } + +--- throws clause + } + + + /** + * 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()! + */ +--- yypushback decl (contains zzScanError exception) + if ( number > yylength() ) + zzScanError(ZZ_PUSHBACK_2BIG); + + zzMarkedPos -= number; + } + + +--- zzDoEOF + /** + * 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 + */ +--- yylex declaration + int zzInput; + int zzAction; + + // cached fields: + int zzCurrentPosL; + int zzMarkedPosL; + int zzEndReadL = zzEndRead; + CharSequence zzBufferL = zzBuffer; + char[] zzBufferArrayL = zzBufferArray; + char [] zzCMapL = ZZ_CMAP; + +--- local declarations + + while (true) { + zzMarkedPosL = zzMarkedPos; + +--- start admin (line, char, col count) + zzAction = -1; + + zzCurrentPosL = zzCurrentPos = zzStartRead = zzMarkedPosL; + +--- start admin (lexstate etc) + + zzForAction: { + while (true) { + +--- next input, line, col, char count, next transition, isFinal action + zzAction = zzState; + zzMarkedPosL = zzCurrentPosL; +--- line count update + } + + } + } + + // store back cached position + zzMarkedPos = zzMarkedPosL; +--- char count update + +--- actions + default: + if (zzInput == YYEOF && zzStartRead == zzCurrentPos) { + zzAtEOF = true; +--- eofvalue + } + else { +--- no match + } + } + } + } + +--- main + +} diff --git a/simple_plugin/src/com/simpleplugin/CreatePropertyQuickFix.java b/simple_plugin/src/com/simpleplugin/CreatePropertyQuickFix.java new file mode 100644 index 000000000..add125e7d --- /dev/null +++ b/simple_plugin/src/com/simpleplugin/CreatePropertyQuickFix.java @@ -0,0 +1,99 @@ +package com.simpleplugin; + +import com.intellij.codeInsight.intention.impl.BaseIntentionAction; +import com.intellij.lang.ASTNode; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.command.WriteCommandAction; +import com.intellij.openapi.editor.*; +import com.intellij.openapi.fileChooser.FileChooser; +import com.intellij.openapi.fileChooser.FileChooserDescriptor; +import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory; +import com.intellij.openapi.fileEditor.FileEditorManager; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.pom.Navigatable; +import com.intellij.psi.PsiFile; +import com.intellij.psi.PsiManager; +import com.intellij.psi.search.FileTypeIndex; +import com.intellij.psi.search.GlobalSearchScope; +import com.intellij.util.IncorrectOperationException; +import com.intellij.util.indexing.FileBasedIndex; +import com.simpleplugin.psi.SimpleElementFactory; +import com.simpleplugin.psi.SimpleFile; +import com.simpleplugin.psi.SimpleProperty; +import com.simpleplugin.psi.SimpleTypes; +import org.jetbrains.annotations.NotNull; + +import java.util.Collection; + +class CreatePropertyQuickFix extends BaseIntentionAction { + private String key; + + CreatePropertyQuickFix(String key) { + this.key = key; + } + + @NotNull + @Override + public String getText() { + return "Create property"; + } + + @NotNull + @Override + public String getFamilyName() { + return "Simple properties"; + } + + @Override + public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) { + return true; + } + + @Override + public void invoke(@NotNull final Project project, final Editor editor, PsiFile file) throws IncorrectOperationException { + ApplicationManager.getApplication().invokeLater(new Runnable() { + @Override + public void run() { + Collection virtualFiles = FileBasedIndex.getInstance().getContainingFiles(FileTypeIndex.NAME, SimpleFileType.INSTANCE, + GlobalSearchScope.allScope(project)); + if (virtualFiles.size() == 1) { + createProperty(project, virtualFiles.iterator().next()); + } else { + final FileChooserDescriptor descriptor = FileChooserDescriptorFactory.createSingleFileDescriptor(SimpleFileType.INSTANCE); + descriptor.setRoots(project.getBaseDir()); + final VirtualFile file = FileChooser.chooseFile(descriptor, project, null); + if (file != null) { + createProperty(project, file); + } + } + } + }); + } + + private void createProperty(final Project project, final VirtualFile file) { + new WriteCommandAction.Simple(project) { + @Override + public void run() { + SimpleFile simpleFile = (SimpleFile) PsiManager.getInstance(project).findFile(file); + ASTNode lastChildNode = simpleFile.getNode().getLastChildNode(); + if (lastChildNode != null && !lastChildNode.getElementType().equals(SimpleTypes.CRLF)) { + simpleFile.getNode().addChild(SimpleElementFactory.createCRLF(project).getNode()); + } + // IMPORTANT: change spaces to escaped spaces or the new node will only have the first word for the key + SimpleProperty property = SimpleElementFactory.createProperty(project, key.replaceAll(" ", "\\\\ "), ""); + simpleFile.getNode().addChild(property.getNode()); + ((Navigatable) property.getLastChild().getNavigationElement()).navigate(true); + FileEditorManager.getInstance(project).getSelectedTextEditor().getCaretModel(). + moveCaretRelatively(2, 0, false, false, false); + + // almost the same thing but manipulating plain text of the document instead of PSI +// FileEditorManager.getInstance(project).openFile(file, true); +// final Editor editor = FileEditorManager.getInstance(project).getSelectedTextEditor(); +// final Document document = editor.getDocument(); +// document.insertString(document.getTextLength(), "\n" + key.replaceAll(" ", "\\\\ ") + " = "); +// editor.getCaretModel().getPrimaryCaret().moveToOffset(document.getTextLength()); + } + }.execute(); + } +} diff --git a/simple_plugin/src/com/simpleplugin/Simple.bnf b/simple_plugin/src/com/simpleplugin/Simple.bnf new file mode 100644 index 000000000..d5ca3cef2 --- /dev/null +++ b/simple_plugin/src/com/simpleplugin/Simple.bnf @@ -0,0 +1,23 @@ +{ + parserClass="com.simpleplugin.parser.SimpleParser" + + extends="com.intellij.extapi.psi.ASTWrapperPsiElement" + + psiClassPrefix="Simple" + psiImplClassSuffix="Impl" + psiPackage="com.simpleplugin.psi" + psiImplPackage="com.simpleplugin.psi.impl" + + elementTypeHolderClass="com.simpleplugin.psi.SimpleTypes" + elementTypeClass="com.simpleplugin.psi.SimpleElementType" + tokenTypeClass="com.simpleplugin.psi.SimpleTokenType" + + 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/simple_plugin/src/com/simpleplugin/Simple.flex b/simple_plugin/src/com/simpleplugin/Simple.flex new file mode 100644 index 000000000..89dabf207 --- /dev/null +++ b/simple_plugin/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\\] | "\\ " + +%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}({CRLF}|{WHITE_SPACE})+ { yybegin(YYINITIAL); return TokenType.WHITE_SPACE; } + + {WHITE_SPACE}+ { yybegin(WAITING_VALUE); return TokenType.WHITE_SPACE; } + + {FIRST_VALUE_CHARACTER}{VALUE_CHARACTER}* { yybegin(YYINITIAL); return SimpleTypes.VALUE; } + +({CRLF}|{WHITE_SPACE})+ { yybegin(YYINITIAL); return TokenType.WHITE_SPACE; } + +{WHITE_SPACE}+ { yybegin(YYINITIAL); return TokenType.WHITE_SPACE; } + +. { return TokenType.BAD_CHARACTER; } diff --git a/simple_plugin/src/com/simpleplugin/SimpleAnnotator.java b/simple_plugin/src/com/simpleplugin/SimpleAnnotator.java new file mode 100644 index 000000000..e0425c5cc --- /dev/null +++ b/simple_plugin/src/com/simpleplugin/SimpleAnnotator.java @@ -0,0 +1,41 @@ +package com.simpleplugin; + +import com.intellij.lang.annotation.Annotation; +import com.intellij.lang.annotation.AnnotationHolder; +import com.intellij.lang.annotation.Annotator; +import com.intellij.openapi.editor.DefaultLanguageHighlighterColors; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.TextRange; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiLiteralExpression; +import com.simpleplugin.psi.SimpleProperty; +import org.jetbrains.annotations.NotNull; + +import java.util.List; + +public class SimpleAnnotator implements Annotator { + @Override + public void annotate(@NotNull final PsiElement element, @NotNull AnnotationHolder holder) { + if (element instanceof PsiLiteralExpression) { + PsiLiteralExpression literalExpression = (PsiLiteralExpression) element; + String value = literalExpression.getValue() instanceof String ? (String)literalExpression.getValue() : null; + + if (value != null && value.startsWith("simple"+":")) { + Project project = element.getProject(); + String key = value.substring(7); + List properties = SimpleUtil.findProperties(project, key); + if (properties.size() == 1) { + TextRange range = new TextRange(element.getTextRange().getStartOffset() + 7, + element.getTextRange().getStartOffset() + 7); + Annotation annotation = holder.createInfoAnnotation(range, null); + annotation.setTextAttributes(DefaultLanguageHighlighterColors.LINE_COMMENT); + } else if (properties.size() == 0) { + TextRange range = new TextRange(element.getTextRange().getStartOffset() + 8, + element.getTextRange().getEndOffset()); + holder.createErrorAnnotation(range, "Unresolved property"). + registerFix(new CreatePropertyQuickFix(key)); + } + } + } + } +} diff --git a/simple_plugin/src/com/simpleplugin/SimpleBlock.java b/simple_plugin/src/com/simpleplugin/SimpleBlock.java new file mode 100644 index 000000000..9c1b723a0 --- /dev/null +++ b/simple_plugin/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; + } +} diff --git a/simple_plugin/src/com/simpleplugin/SimpleChooseByNameContributor.java b/simple_plugin/src/com/simpleplugin/SimpleChooseByNameContributor.java new file mode 100644 index 000000000..1ec9f06ed --- /dev/null +++ b/simple_plugin/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/simple_plugin/src/com/simpleplugin/SimpleCodeStyleSettings.java b/simple_plugin/src/com/simpleplugin/SimpleCodeStyleSettings.java new file mode 100644 index 000000000..bb62cdb4c --- /dev/null +++ b/simple_plugin/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/simple_plugin/src/com/simpleplugin/SimpleCodeStyleSettingsProvider.java b/simple_plugin/src/com/simpleplugin/SimpleCodeStyleSettingsProvider.java new file mode 100644 index 000000000..f19dc35b8 --- /dev/null +++ b/simple_plugin/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/simple_plugin/src/com/simpleplugin/SimpleColorSettingsPage.java b/simple_plugin/src/com/simpleplugin/SimpleColorSettingsPage.java new file mode 100644 index 000000000..489aede15 --- /dev/null +++ b/simple_plugin/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/simple_plugin/src/com/simpleplugin/SimpleCommenter.java b/simple_plugin/src/com/simpleplugin/SimpleCommenter.java new file mode 100644 index 000000000..f9f0e24a1 --- /dev/null +++ b/simple_plugin/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/simple_plugin/src/com/simpleplugin/SimpleCompletionContributor.java b/simple_plugin/src/com/simpleplugin/SimpleCompletionContributor.java new file mode 100644 index 000000000..1ca45e01e --- /dev/null +++ b/simple_plugin/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/simple_plugin/src/com/simpleplugin/SimpleFileType.java b/simple_plugin/src/com/simpleplugin/SimpleFileType.java new file mode 100644 index 000000000..3d3714b4d --- /dev/null +++ b/simple_plugin/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/simple_plugin/src/com/simpleplugin/SimpleFileTypeFactory.java b/simple_plugin/src/com/simpleplugin/SimpleFileTypeFactory.java new file mode 100644 index 000000000..2a09d45be --- /dev/null +++ b/simple_plugin/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/simple_plugin/src/com/simpleplugin/SimpleFilterLexer.java b/simple_plugin/src/com/simpleplugin/SimpleFilterLexer.java new file mode 100644 index 000000000..78de00162 --- /dev/null +++ b/simple_plugin/src/com/simpleplugin/SimpleFilterLexer.java @@ -0,0 +1,19 @@ +package com.simpleplugin; + +import com.intellij.lexer.Lexer; +import com.intellij.psi.impl.cache.impl.BaseFilterLexer; +import com.intellij.psi.impl.cache.impl.OccurrenceConsumer; +import com.intellij.psi.search.UsageSearchContext; + +public class SimpleFilterLexer extends BaseFilterLexer { + public SimpleFilterLexer(final Lexer originalLexer, final OccurrenceConsumer table) { + super(originalLexer, table); + } + + @Override + public void advance() { + scanWordsInToken(UsageSearchContext.IN_COMMENTS, false, false); + advanceTodoItemCountsInToken(); + myDelegate.advance(); + } +} diff --git a/simple_plugin/src/com/simpleplugin/SimpleFindUsagesProvider.java b/simple_plugin/src/com/simpleplugin/SimpleFindUsagesProvider.java new file mode 100644 index 000000000..c766cd3b9 --- /dev/null +++ b/simple_plugin/src/com/simpleplugin/SimpleFindUsagesProvider.java @@ -0,0 +1,65 @@ +package com.simpleplugin; + +import com.intellij.lang.cacheBuilder.DefaultWordsScanner; +import com.intellij.lang.cacheBuilder.WordsScanner; +import com.intellij.lang.findUsages.FindUsagesProvider; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiNamedElement; +import com.intellij.psi.tree.TokenSet; +import com.simpleplugin.psi.SimpleProperty; +import com.simpleplugin.psi.SimpleTypes; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class SimpleFindUsagesProvider implements FindUsagesProvider { + private static final DefaultWordsScanner WORDS_SCANNER = + new DefaultWordsScanner(new SimpleLexerAdapter(), + 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 null; + } + + @NotNull + @Override + public String getType(@NotNull PsiElement element) { + if (element instanceof SimpleProperty) { + return "simple property"; + } else { + return ""; + } + } + + @NotNull + @Override + public String getDescriptiveName(@NotNull PsiElement element) { + if (element instanceof SimpleProperty) { + return ((SimpleProperty) element).getKey(); + } else { + return ""; + } + } + + @NotNull + @Override + public String getNodeText(@NotNull PsiElement element, boolean useFullName) { + if (element instanceof SimpleProperty) { + return ((SimpleProperty) element).getKey() + ":" + ((SimpleProperty) element).getValue(); + } else { + return ""; + } + } +} \ No newline at end of file diff --git a/simple_plugin/src/com/simpleplugin/SimpleFoldingBuilder.java b/simple_plugin/src/com/simpleplugin/SimpleFoldingBuilder.java new file mode 100644 index 000000000..745726933 --- /dev/null +++ b/simple_plugin/src/com/simpleplugin/SimpleFoldingBuilder.java @@ -0,0 +1,65 @@ +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 = literalExpression.getValue() instanceof String ? (String)literalExpression.getValue() : null; + + if (value != null && value.startsWith("simple:")) { + Project project = literalExpression.getProject(); + String key = value.substring(7); + final List properties = SimpleUtil.findProperties(project, key); + 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() { + // IMPORTANT: keys can come with no values, so a test for null is needed + // IMPORTANT: Convert embedded \n to backslash n, so that the string will look like it has LF embedded in it and embedded " to escaped " + String valueOf = properties.get(0).getValue(); + return valueOf == null ? "" : valueOf.replaceAll("\n","\\n").replaceAll("\"","\\\\\""); + } + }); + } + } + } + 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; + } +} diff --git a/simple_plugin/src/com/simpleplugin/SimpleFormattingModelBuilder.java b/simple_plugin/src/com/simpleplugin/SimpleFormattingModelBuilder.java new file mode 100644 index 000000000..b20468c77 --- /dev/null +++ b/simple_plugin/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, SimpleLanguage.INSTANCE). + around(SimpleTypes.SEPARATOR).spaceIf(settings.SPACE_AROUND_ASSIGNMENT_OPERATORS). + before(SimpleTypes.PROPERTY).none(); + } + + @Nullable + @Override + public TextRange getRangeAffectingIndent(PsiFile file, int offset, ASTNode elementAtOffset) { + return null; + } +} \ No newline at end of file diff --git a/simple_plugin/src/com/simpleplugin/SimpleIcons.java b/simple_plugin/src/com/simpleplugin/SimpleIcons.java new file mode 100644 index 000000000..2719cf69e --- /dev/null +++ b/simple_plugin/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/simple_plugin/src/com/simpleplugin/SimpleIdIndexer.java b/simple_plugin/src/com/simpleplugin/SimpleIdIndexer.java new file mode 100644 index 000000000..bfc0a75ac --- /dev/null +++ b/simple_plugin/src/com/simpleplugin/SimpleIdIndexer.java @@ -0,0 +1,17 @@ +package com.simpleplugin; + +import com.intellij.lexer.Lexer; +import com.intellij.psi.impl.cache.impl.OccurrenceConsumer; +import com.intellij.psi.impl.cache.impl.id.LexerBasedIdIndexer; + +public class SimpleIdIndexer extends LexerBasedIdIndexer { + + public static Lexer createIndexingLexer(OccurrenceConsumer consumer) { + return new SimpleFilterLexer(new SimpleLexerAdapter(), consumer); + } + + @Override + public Lexer createLexer(final OccurrenceConsumer consumer) { + return createIndexingLexer(consumer); + } +} diff --git a/simple_plugin/src/com/simpleplugin/SimpleLanguage.java b/simple_plugin/src/com/simpleplugin/SimpleLanguage.java new file mode 100644 index 000000000..425e59019 --- /dev/null +++ b/simple_plugin/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/simple_plugin/src/com/simpleplugin/SimpleLanguageCodeStyleSettingsProvider.java b/simple_plugin/src/com/simpleplugin/SimpleLanguageCodeStyleSettingsProvider.java new file mode 100644 index 000000000..5df02eb89 --- /dev/null +++ b/simple_plugin/src/com/simpleplugin/SimpleLanguageCodeStyleSettingsProvider.java @@ -0,0 +1,43 @@ +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"); + } + } + + @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" + + "\n" + + "\n" + + "\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"; + } +} diff --git a/simple_plugin/src/com/simpleplugin/SimpleLexer.java b/simple_plugin/src/com/simpleplugin/SimpleLexer.java new file mode 100644 index 000000000..b18e92e45 --- /dev/null +++ b/simple_plugin/src/com/simpleplugin/SimpleLexer.java @@ -0,0 +1,509 @@ +/* The following code was generated by JFlex 1.4.3 on 02/09/15 10:04 AM */ + +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 02/09/15 10:04 AM from the specification file + * /Users/vlad/src/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\1\2\1\3\1\4\1\5\1\6\1\7"+ + "\1\3\1\7\2\0\1\6"; + + private static int [] zzUnpackAction() { + int [] result = new int[14]; + 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\110\0\121\0\132\0\44\0\121\0\143"; + + private static int [] zzUnpackRowMap() { + int [] result = new int[14]; + 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\3\4\1\5\2\4\1\6\1\7\1\10\2\4"+ + "\1\11\1\12\2\13\2\10\1\3\3\0\1\14\2\0"+ + "\1\3\2\0\3\4\1\0\2\4\7\0\1\3\3\0"+ + "\1\6\2\0\6\6\11\0\1\10\2\0\1\10\1\15"+ + "\1\10\1\0\3\10\2\4\1\11\1\15\1\11\1\13"+ + "\4\10\1\16\6\10\1\0\2\4\1\13\1\0\2\13"+ + "\2\0\2\10\1\0\1\10\1\15\1\10\1\0\2\10"; + + private static int [] zzUnpackTrans() { + int [] result = new int[108]; + 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\4\1\1\11\4\1\2\0\1\1"; + + private static int [] zzUnpackAttribute() { + int [] result = new int[14]; + 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 = (zzBufferArrayL != null ? zzBufferArrayL[zzCurrentPosL++] : 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 = (zzBufferArrayL != null ? zzBufferArrayL[zzCurrentPosL++] : 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 6: + { yybegin(YYINITIAL); return SimpleTypes.VALUE; + } + case 8: break; + case 5: + { yybegin(WAITING_VALUE); return SimpleTypes.SEPARATOR; + } + case 9: break; + case 4: + { yybegin(YYINITIAL); return SimpleTypes.COMMENT; + } + case 10: break; + case 3: + { return TokenType.BAD_CHARACTER; + } + case 11: break; + case 2: + { yybegin(YYINITIAL); return TokenType.WHITE_SPACE; + } + case 12: break; + case 7: + { yybegin(WAITING_VALUE); return TokenType.WHITE_SPACE; + } + case 13: break; + case 1: + { yybegin(YYINITIAL); return SimpleTypes.KEY; + } + case 14: break; + default: + if (zzInput == YYEOF && zzStartRead == zzCurrentPos) { + zzAtEOF = true; + zzDoEOF(); + return null; + } + else { + zzScanError(ZZ_NO_MATCH); + } + } + } + } + + +} diff --git a/simple_plugin/src/com/simpleplugin/SimpleLexerAdapter.java b/simple_plugin/src/com/simpleplugin/SimpleLexerAdapter.java new file mode 100644 index 000000000..662acb59a --- /dev/null +++ b/simple_plugin/src/com/simpleplugin/SimpleLexerAdapter.java @@ -0,0 +1,11 @@ +package com.simpleplugin; + +import com.intellij.lexer.FlexAdapter; + +import java.io.Reader; + +public class SimpleLexerAdapter extends FlexAdapter { + public SimpleLexerAdapter() { + super(new SimpleLexer((Reader) null)); + } +} diff --git a/simple_plugin/src/com/simpleplugin/SimpleLineMarkerProvider.java b/simple_plugin/src/com/simpleplugin/SimpleLineMarkerProvider.java new file mode 100644 index 000000000..d6dc43d96 --- /dev/null +++ b/simple_plugin/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 = literalExpression.getValue() instanceof String ? (String)literalExpression.getValue() : null; + 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)); + } + } + } + } +} diff --git a/simple_plugin/src/com/simpleplugin/SimpleParserDefinition.java b/simple_plugin/src/com/simpleplugin/SimpleParserDefinition.java new file mode 100644 index 000000000..c9de1df95 --- /dev/null +++ b/simple_plugin/src/com/simpleplugin/SimpleParserDefinition.java @@ -0,0 +1,69 @@ +package com.simpleplugin; + +import com.intellij.lang.ASTNode; +import com.intellij.lang.Language; +import com.intellij.lang.ParserDefinition; +import com.intellij.lang.PsiParser; +import com.intellij.lexer.Lexer; +import com.intellij.openapi.project.Project; +import com.intellij.psi.FileViewProvider; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiFile; +import com.intellij.psi.TokenType; +import com.intellij.psi.tree.IFileElementType; +import com.intellij.psi.tree.TokenSet; +import com.simpleplugin.parser.SimpleParser; +import com.simpleplugin.psi.SimpleFile; +import com.simpleplugin.psi.SimpleTypes; +import org.jetbrains.annotations.NotNull; + +public class SimpleParserDefinition implements ParserDefinition{ + public static final TokenSet WHITE_SPACES = TokenSet.create(TokenType.WHITE_SPACE); + public static final TokenSet COMMENTS = TokenSet.create(SimpleTypes.COMMENT); + + public static final IFileElementType FILE = new IFileElementType(Language.findInstance(SimpleLanguage.class)); + + @NotNull + @Override + public Lexer createLexer(Project project) { + return new SimpleLexerAdapter(); + } + + @NotNull + public TokenSet getWhitespaceTokens() { + return WHITE_SPACES; + } + + @NotNull + public TokenSet getCommentTokens() { + return COMMENTS; + } + + @NotNull + public TokenSet getStringLiteralElements() { + return TokenSet.EMPTY; + } + + @NotNull + public PsiParser createParser(final Project project) { + return new SimpleParser(); + } + + @Override + public IFileElementType getFileNodeType() { + return FILE; + } + + public PsiFile createFile(FileViewProvider viewProvider) { + return new SimpleFile(viewProvider); + } + + public SpaceRequirements spaceExistanceTypeBetweenTokens(ASTNode left, ASTNode right) { + return SpaceRequirements.MAY; + } + + @NotNull + public PsiElement createElement(ASTNode node) { + return SimpleTypes.Factory.createElement(node); + } +} \ No newline at end of file diff --git a/simple_plugin/src/com/simpleplugin/SimpleRefactoringSupportProvider.java b/simple_plugin/src/com/simpleplugin/SimpleRefactoringSupportProvider.java new file mode 100644 index 000000000..8040801d7 --- /dev/null +++ b/simple_plugin/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/simple_plugin/src/com/simpleplugin/SimpleReference.java b/simple_plugin/src/com/simpleplugin/SimpleReference.java new file mode 100644 index 000000000..cbb9b295d --- /dev/null +++ b/simple_plugin/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/simple_plugin/src/com/simpleplugin/SimpleReferenceContributor.java b/simple_plugin/src/com/simpleplugin/SimpleReferenceContributor.java new file mode 100644 index 000000000..1e1dbb3a8 --- /dev/null +++ b/simple_plugin/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(@NotNull 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 value = literalExpression.getValue() instanceof String ? (String)literalExpression.getValue() : null; + if (value != null && value.startsWith("simple"+":")) { + return new PsiReference[]{new SimpleReference(element, new TextRange(8, value.length() + 1))}; + } + return new PsiReference[0]; + } + }); + } +} diff --git a/simple_plugin/src/com/simpleplugin/SimpleStructureViewElement.java b/simple_plugin/src/com/simpleplugin/SimpleStructureViewElement.java new file mode 100644 index 000000000..29ba22956 --- /dev/null +++ b/simple_plugin/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/simple_plugin/src/com/simpleplugin/SimpleStructureViewFactory.java b/simple_plugin/src/com/simpleplugin/SimpleStructureViewFactory.java new file mode 100644 index 000000000..937fe85a6 --- /dev/null +++ b/simple_plugin/src/com/simpleplugin/SimpleStructureViewFactory.java @@ -0,0 +1,24 @@ +package com.simpleplugin; + +import com.intellij.ide.structureView.StructureViewBuilder; +import com.intellij.ide.structureView.StructureViewModel; +import com.intellij.ide.structureView.TreeBasedStructureViewBuilder; +import com.intellij.lang.PsiStructureViewFactory; +import com.intellij.openapi.editor.Editor; +import com.intellij.psi.PsiFile; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class SimpleStructureViewFactory implements PsiStructureViewFactory { + @Nullable + @Override + public StructureViewBuilder getStructureViewBuilder(final PsiFile psiFile) { + return new TreeBasedStructureViewBuilder() { + @NotNull + @Override + public StructureViewModel createStructureViewModel(@Nullable Editor editor) { + return new SimpleStructureViewModel(psiFile); + } + }; + } +} \ No newline at end of file diff --git a/simple_plugin/src/com/simpleplugin/SimpleStructureViewModel.java b/simple_plugin/src/com/simpleplugin/SimpleStructureViewModel.java new file mode 100644 index 000000000..3d2318087 --- /dev/null +++ b/simple_plugin/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/simple_plugin/src/com/simpleplugin/SimpleSyntaxHighlighter.java b/simple_plugin/src/com/simpleplugin/SimpleSyntaxHighlighter.java new file mode 100644 index 000000000..530581d58 --- /dev/null +++ b/simple_plugin/src/com/simpleplugin/SimpleSyntaxHighlighter.java @@ -0,0 +1,52 @@ +package com.simpleplugin; + +import com.intellij.lexer.Lexer; +import com.intellij.openapi.editor.DefaultLanguageHighlighterColors; +import com.intellij.openapi.editor.HighlighterColors; +import com.intellij.openapi.editor.colors.TextAttributesKey; +import com.intellij.openapi.fileTypes.SyntaxHighlighterBase; +import com.intellij.psi.TokenType; +import com.intellij.psi.tree.IElementType; +import com.simpleplugin.psi.SimpleTypes; +import org.jetbrains.annotations.NotNull; + +import static com.intellij.openapi.editor.colors.TextAttributesKey.createTextAttributesKey; + +public class SimpleSyntaxHighlighter extends SyntaxHighlighterBase { + public static final TextAttributesKey SEPARATOR = createTextAttributesKey("SIMPLE_SEPARATOR", DefaultLanguageHighlighterColors.OPERATION_SIGN); + public static final TextAttributesKey KEY = createTextAttributesKey("SIMPLE_KEY", DefaultLanguageHighlighterColors.KEYWORD); + public static final TextAttributesKey VALUE = createTextAttributesKey("SIMPLE_VALUE", DefaultLanguageHighlighterColors.STRING); + public static final TextAttributesKey COMMENT = createTextAttributesKey("SIMPLE_COMMENT", DefaultLanguageHighlighterColors.LINE_COMMENT); + public static final TextAttributesKey BAD_CHARACTER = createTextAttributesKey("SIMPLE_BAD_CHARACTER", HighlighterColors.BAD_CHARACTER); + + private static final TextAttributesKey[] BAD_CHAR_KEYS = new TextAttributesKey[]{BAD_CHARACTER}; + private static final TextAttributesKey[] SEPARATOR_KEYS = new TextAttributesKey[]{SEPARATOR}; + private static final TextAttributesKey[] KEY_KEYS = new TextAttributesKey[]{KEY}; + private static final TextAttributesKey[] VALUE_KEYS = new TextAttributesKey[]{VALUE}; + private static final TextAttributesKey[] COMMENT_KEYS = new TextAttributesKey[]{COMMENT}; + private static final TextAttributesKey[] EMPTY_KEYS = new TextAttributesKey[0]; + + @NotNull + @Override + public Lexer getHighlightingLexer() { + return new SimpleLexerAdapter(); + } + + @NotNull + @Override + public TextAttributesKey[] getTokenHighlights(IElementType tokenType) { + if (tokenType.equals(SimpleTypes.SEPARATOR)) { + return SEPARATOR_KEYS; + } else if (tokenType.equals(SimpleTypes.KEY)) { + return KEY_KEYS; + } else if (tokenType.equals(SimpleTypes.VALUE)) { + return VALUE_KEYS; + } else if (tokenType.equals(SimpleTypes.COMMENT)) { + return COMMENT_KEYS; + } else if (tokenType.equals(TokenType.BAD_CHARACTER)) { + return BAD_CHAR_KEYS; + } else { + return EMPTY_KEYS; + } + } +} \ No newline at end of file diff --git a/simple_plugin/src/com/simpleplugin/SimpleSyntaxHighlighterFactory.java b/simple_plugin/src/com/simpleplugin/SimpleSyntaxHighlighterFactory.java new file mode 100644 index 000000000..e3a6fe428 --- /dev/null +++ b/simple_plugin/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/simple_plugin/src/com/simpleplugin/SimpleTodoIndexer.java b/simple_plugin/src/com/simpleplugin/SimpleTodoIndexer.java new file mode 100644 index 000000000..505c0acce --- /dev/null +++ b/simple_plugin/src/com/simpleplugin/SimpleTodoIndexer.java @@ -0,0 +1,12 @@ +package com.simpleplugin; + +import com.intellij.lexer.Lexer; +import com.intellij.psi.impl.cache.impl.OccurrenceConsumer; +import com.intellij.psi.impl.cache.impl.todo.LexerBasedTodoIndexer; + +public class SimpleTodoIndexer extends LexerBasedTodoIndexer { + @Override + public Lexer createLexer(OccurrenceConsumer consumer) { + return SimpleIdIndexer.createIndexingLexer(consumer); + } +} diff --git a/simple_plugin/src/com/simpleplugin/SimpleUtil.java b/simple_plugin/src/com/simpleplugin/SimpleUtil.java new file mode 100644 index 000000000..c2bb98218 --- /dev/null +++ b/simple_plugin/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/simple_plugin/src/com/simpleplugin/icons/jar-gray.png b/simple_plugin/src/com/simpleplugin/icons/jar-gray.png new file mode 100644 index 000000000..4760e60a0 Binary files /dev/null and b/simple_plugin/src/com/simpleplugin/icons/jar-gray.png differ diff --git a/simple_plugin/src/com/simpleplugin/psi/SimpleElementFactory.java b/simple_plugin/src/com/simpleplugin/psi/SimpleElementFactory.java new file mode 100644 index 000000000..91e10ba09 --- /dev/null +++ b/simple_plugin/src/com/simpleplugin/psi/SimpleElementFactory.java @@ -0,0 +1,29 @@ +package com.simpleplugin.psi; + +import com.intellij.openapi.project.Project; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiFileFactory; +import com.simpleplugin.SimpleFileType; + +public class SimpleElementFactory { + public static SimpleProperty createProperty(Project project, String name, String value) { + final SimpleFile file = createFile(project, name + " = " + value); + return (SimpleProperty) file.getFirstChild(); + } + + public static SimpleProperty createProperty(Project project, String name) { + final SimpleFile file = createFile(project, name); + return (SimpleProperty) file.getFirstChild(); + } + + public static PsiElement createCRLF(Project project) { + final SimpleFile file = createFile(project, "\n"); + return file.getFirstChild(); + } + + public static SimpleFile createFile(Project project, String text) { + String name = "dummy.simple"; + return (SimpleFile) PsiFileFactory.getInstance(project). + createFileFromText(name, SimpleFileType.INSTANCE, text); + } +} \ No newline at end of file diff --git a/simple_plugin/src/com/simpleplugin/psi/SimpleElementType.java b/simple_plugin/src/com/simpleplugin/psi/SimpleElementType.java new file mode 100644 index 000000000..34e5c3464 --- /dev/null +++ b/simple_plugin/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/simple_plugin/src/com/simpleplugin/psi/SimpleFile.java b/simple_plugin/src/com/simpleplugin/psi/SimpleFile.java new file mode 100644 index 000000000..21c8adc3b --- /dev/null +++ b/simple_plugin/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/simple_plugin/src/com/simpleplugin/psi/SimpleNamedElement.java b/simple_plugin/src/com/simpleplugin/psi/SimpleNamedElement.java new file mode 100644 index 000000000..8c4d9260c --- /dev/null +++ b/simple_plugin/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/simple_plugin/src/com/simpleplugin/psi/SimpleTokenType.java b/simple_plugin/src/com/simpleplugin/psi/SimpleTokenType.java new file mode 100644 index 000000000..6bdb585b4 --- /dev/null +++ b/simple_plugin/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/simple_plugin/src/com/simpleplugin/psi/impl/SimpleNamedElementImpl.java b/simple_plugin/src/com/simpleplugin/psi/impl/SimpleNamedElementImpl.java new file mode 100644 index 000000000..dcb333a62 --- /dev/null +++ b/simple_plugin/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/simple_plugin/src/com/simpleplugin/psi/impl/SimplePsiImplUtil.java b/simple_plugin/src/com/simpleplugin/psi/impl/SimplePsiImplUtil.java new file mode 100644 index 000000000..3e58dc070 --- /dev/null +++ b/simple_plugin/src/com/simpleplugin/psi/impl/SimplePsiImplUtil.java @@ -0,0 +1,81 @@ +package com.simpleplugin.psi.impl; + +import com.intellij.lang.ASTNode; +import com.intellij.navigation.ItemPresentation; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiFile; +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 java.lang.String; + +import javax.swing.*; + +public class SimplePsiImplUtil { + public static String getKey(SimpleProperty element) { + ASTNode keyNode = element.getNode().findChildByType(SimpleTypes.KEY); + if (keyNode != null) { + // IMPORTANT: Convert embedded escaped spaces to simple spaces + return keyNode.getText().replaceAll("\\\\ "," "); + } 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() { + PsiFile containingFile = element.getContainingFile(); + return containingFile == null ? null : containingFile.getName(); + } + + @Nullable + @Override + public Icon getIcon(boolean unused) { + return SimpleIcons.FILE; + } + }; + } +} diff --git a/simple_plugin/testData/AnnotatorTestData.java b/simple_plugin/testData/AnnotatorTestData.java new file mode 100644 index 000000000..699bba52c --- /dev/null +++ b/simple_plugin/testData/AnnotatorTestData.java @@ -0,0 +1,7 @@ +public class Test { + public static void main(String[] args) { + System.out.println("simple:website"); + System.out.println("simple:key with spaces"); + System.out.println("simple:websit"); + } +} diff --git a/simple_plugin/testData/CompleteTestData.java b/simple_plugin/testData/CompleteTestData.java new file mode 100644 index 000000000..4a883daec --- /dev/null +++ b/simple_plugin/testData/CompleteTestData.java @@ -0,0 +1,5 @@ +public class Test { + public static void main(String[] args) { + System.out.println("simple:"); + } +} diff --git a/simple_plugin/testData/DefaultTestData.simple b/simple_plugin/testData/DefaultTestData.simple new file mode 100644 index 000000000..ebe624812 --- /dev/null +++ b/simple_plugin/testData/DefaultTestData.simple @@ -0,0 +1,14 @@ +# You are reading the ".properties" entry. +! The exclamation mark can also mark text as comments. +website = http://en.wikipedia.org/ + + +language = English +# The backslash below tells the application to continue reading +# the value onto the next line. +message = Welcome to \ + Wikipedia! +# Add spaces to the key +key\ with\ spaces = This is the value that could be looked up with the key "key with spaces". +# Unicode +tab : \u0009 diff --git a/simple_plugin/testData/FindUsagesTestData.java b/simple_plugin/testData/FindUsagesTestData.java new file mode 100644 index 000000000..401d8c782 --- /dev/null +++ b/simple_plugin/testData/FindUsagesTestData.java @@ -0,0 +1,5 @@ +public class Test { + public static void main(String[] args) { + System.out.println("simple:key with spaces"); + } +} diff --git a/simple_plugin/testData/FindUsagesTestData.simple b/simple_plugin/testData/FindUsagesTestData.simple new file mode 100644 index 000000000..2f31ab9e2 --- /dev/null +++ b/simple_plugin/testData/FindUsagesTestData.simple @@ -0,0 +1,13 @@ +# You are reading the ".properties" entry. +! The exclamation mark can also mark text as comments. +website = http://en.wikipedia.org/ + +language = English +# The backslash below tells the application to continue reading +# the value onto the next line. +message = Welcome to \ + Wikipedia! +# Add spaces to the key +key\ with\ spaces = This is the value that could be looked up with the key "key with spaces". +# Unicode +tab : \u0009 diff --git a/simple_plugin/testData/FoldingTestData.java b/simple_plugin/testData/FoldingTestData.java new file mode 100644 index 000000000..1920b5fae --- /dev/null +++ b/simple_plugin/testData/FoldingTestData.java @@ -0,0 +1,11 @@ +public class Test { + public static void main(String[] args) { + System.out.println("simple:website"); + } + public static void main1(String[] args) { + System.out.println("simple:key with spaces"); + } + public static void main2(String[] args) { + System.out.println("simple:message"); + } +} diff --git a/simple_plugin/testData/FormatterTestData.simple b/simple_plugin/testData/FormatterTestData.simple new file mode 100644 index 000000000..44a78fa68 --- /dev/null +++ b/simple_plugin/testData/FormatterTestData.simple @@ -0,0 +1,15 @@ +# You are reading the ".properties" entry. +! The exclamation mark can also mark text as comments. +website=http://en.wikipedia.org/ + + + +language= English +# The backslash below tells the application to continue reading +# the value onto the next line. +message = Welcome to \ + Wikipedia! +# Add spaces to the key +key\ with\ spaces = This is the value that could be looked up with the key "key with spaces". +# Unicode +tab :\u0009 diff --git a/simple_plugin/testData/ParsingTestData.simple b/simple_plugin/testData/ParsingTestData.simple new file mode 100644 index 000000000..e11fdcef3 --- /dev/null +++ b/simple_plugin/testData/ParsingTestData.simple @@ -0,0 +1,17 @@ +# You are reading the ".properties" entry. +! The exclamation mark can also mark text as comments. +website = http://en.wikipedia.org/ + +language = English +# The backslash below tells the application to continue reading +# the value onto the next line. +message = Welcome to \ + Wikipedia! +# Add spaces to the key +key\ with\ spaces = This is the value that could be looked up with the key "key with spaces". +# Unicode +tab : \u0009 +# test for illegal key attempt +key\ +with\ +endofline = test diff --git a/simple_plugin/testData/ParsingTestData.txt b/simple_plugin/testData/ParsingTestData.txt new file mode 100644 index 000000000..ce7aa544e --- /dev/null +++ b/simple_plugin/testData/ParsingTestData.txt @@ -0,0 +1,63 @@ +Simple File(0,492) + PsiComment(SimpleTokenType.COMMENT)('# You are reading the ".properties" entry.')(0,42) + PsiWhiteSpace('\n')(42,43) + PsiComment(SimpleTokenType.COMMENT)('! The exclamation mark can also mark text as comments.')(43,97) + PsiWhiteSpace('\n')(97,98) + SimplePropertyImpl(PROPERTY)(98,132) + PsiElement(SimpleTokenType.KEY)('website')(98,105) + PsiWhiteSpace(' ')(105,106) + PsiElement(SimpleTokenType.SEPARATOR)('=')(106,107) + PsiWhiteSpace(' ')(107,108) + PsiElement(SimpleTokenType.VALUE)('http://en.wikipedia.org/')(108,132) + PsiWhiteSpace('\n\n')(132,134) + SimplePropertyImpl(PROPERTY)(134,152) + PsiElement(SimpleTokenType.KEY)('language')(134,142) + PsiWhiteSpace(' ')(142,143) + PsiElement(SimpleTokenType.SEPARATOR)('=')(143,144) + PsiWhiteSpace(' ')(144,145) + PsiElement(SimpleTokenType.VALUE)('English')(145,152) + PsiWhiteSpace('\n')(152,153) + PsiComment(SimpleTokenType.COMMENT)('# The backslash below tells the application to continue reading')(153,216) + PsiWhiteSpace('\n')(216,217) + PsiComment(SimpleTokenType.COMMENT)('# the value onto the next line.')(217,248) + PsiWhiteSpace('\n')(248,249) + SimplePropertyImpl(PROPERTY)(249,292) + PsiElement(SimpleTokenType.KEY)('message')(249,256) + PsiWhiteSpace(' ')(256,257) + PsiElement(SimpleTokenType.SEPARATOR)('=')(257,258) + PsiWhiteSpace(' ')(258,259) + PsiElement(SimpleTokenType.VALUE)('Welcome to \\n Wikipedia!')(259,292) + PsiWhiteSpace('\n')(292,293) + PsiComment(SimpleTokenType.COMMENT)('# Add spaces to the key')(293,316) + PsiWhiteSpace('\n')(316,317) + SimplePropertyImpl(PROPERTY)(317,410) + PsiElement(SimpleTokenType.KEY)('key\ with\ spaces')(317,334) + PsiWhiteSpace(' ')(334,335) + PsiElement(SimpleTokenType.SEPARATOR)('=')(335,336) + PsiWhiteSpace(' ')(336,337) + PsiElement(SimpleTokenType.VALUE)('This is the value that could be looked up with the key "key with spaces".')(337,410) + PsiWhiteSpace('\n')(410,411) + PsiComment(SimpleTokenType.COMMENT)('# Unicode')(411,420) + PsiWhiteSpace('\n')(420,421) + SimplePropertyImpl(PROPERTY)(421,433) + PsiElement(SimpleTokenType.KEY)('tab')(421,424) + PsiWhiteSpace(' ')(424,425) + PsiElement(SimpleTokenType.SEPARATOR)(':')(425,426) + PsiWhiteSpace(' ')(426,427) + PsiElement(SimpleTokenType.VALUE)('\u0009')(427,433) + PsiWhiteSpace('\n')(433,434) + PsiComment(SimpleTokenType.COMMENT)('# test for illegal key attempt')(434,464) + PsiWhiteSpace('\n')(464,465) + SimplePropertyImpl(PROPERTY)(465,468) + PsiElement(SimpleTokenType.KEY)('key')(465,468) + PsiErrorElement:, SimpleTokenType.COMMENT, SimpleTokenType.CRLF or SimpleTokenType.SEPARATOR expected, got '\'(468,469) + PsiElement(BAD_CHARACTER)('\')(468,469) + PsiWhiteSpace('\n')(469,470) + PsiElement(SimpleTokenType.KEY)('with')(470,474) + PsiElement(BAD_CHARACTER)('\')(474,475) + PsiWhiteSpace('\n')(475,476) + PsiElement(SimpleTokenType.KEY)('endofline')(476,485) + PsiWhiteSpace(' ')(485,486) + PsiElement(SimpleTokenType.SEPARATOR)('=')(486,487) + PsiWhiteSpace(' ')(487,488) + PsiElement(SimpleTokenType.VALUE)('test')(488,492) diff --git a/simple_plugin/testData/ReferenceTestData.java b/simple_plugin/testData/ReferenceTestData.java new file mode 100644 index 000000000..384347eda --- /dev/null +++ b/simple_plugin/testData/ReferenceTestData.java @@ -0,0 +1,5 @@ +public class Test { + public static void main(String[] args) { + System.out.println("simple:website"); + } +} diff --git a/simple_plugin/testData/RenameTestData.java b/simple_plugin/testData/RenameTestData.java new file mode 100644 index 000000000..384347eda --- /dev/null +++ b/simple_plugin/testData/RenameTestData.java @@ -0,0 +1,5 @@ +public class Test { + public static void main(String[] args) { + System.out.println("simple:website"); + } +} diff --git a/simple_plugin/testData/RenameTestData.simple b/simple_plugin/testData/RenameTestData.simple new file mode 100644 index 000000000..31492ca75 --- /dev/null +++ b/simple_plugin/testData/RenameTestData.simple @@ -0,0 +1,13 @@ +# You are reading the ".properties" entry. +! The exclamation mark can also mark text as comments. +website = http://en.wikipedia.org/ + +language = English +# The backslash below tells the application to continue reading +# the value onto the next line. +message = Welcome to \ + Wikipedia! +# Add spaces to the key +key\ with\ spaces = This is the value that could be looked up with the key "key with spaces". +# Unicode +tab : \u0009 \ No newline at end of file diff --git a/simple_plugin/testData/RenameTestDataAfter.simple b/simple_plugin/testData/RenameTestDataAfter.simple new file mode 100644 index 000000000..71bf7bf73 --- /dev/null +++ b/simple_plugin/testData/RenameTestDataAfter.simple @@ -0,0 +1,13 @@ +# You are reading the ".properties" entry. +! The exclamation mark can also mark text as comments. +websiteUrl = http://en.wikipedia.org/ + +language = English +# The backslash below tells the application to continue reading +# the value onto the next line. +message = Welcome to \ + Wikipedia! +# Add spaces to the key +key\ with\ spaces = This is the value that could be looked up with the key "key with spaces". +# Unicode +tab : \u0009 \ No newline at end of file diff --git a/simple_plugin/tests/com/simpleplugin/SimpleCodeInsightTest.java b/simple_plugin/tests/com/simpleplugin/SimpleCodeInsightTest.java new file mode 100644 index 000000000..29ecc65d6 --- /dev/null +++ b/simple_plugin/tests/com/simpleplugin/SimpleCodeInsightTest.java @@ -0,0 +1,86 @@ +package com.simpleplugin; + +import com.intellij.codeInsight.completion.CompletionType; +import com.intellij.codeInsight.generation.actions.CommentByLineCommentAction; +import com.intellij.openapi.command.WriteCommandAction; +import com.intellij.openapi.vfs.newvfs.impl.VfsRootAccess; +import com.intellij.psi.PsiElement; +import com.intellij.psi.codeStyle.CodeStyleManager; +import com.intellij.psi.codeStyle.CodeStyleSettingsManager; +import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase; +import com.intellij.usageView.UsageInfo; +import com.simpleplugin.psi.SimpleProperty; + +import java.util.Arrays; +import java.util.Collection; +import java.util.List; + +public class SimpleCodeInsightTest extends LightCodeInsightFixtureTestCase { + @Override + protected void setUp() throws Exception { + VfsRootAccess.SHOULD_PERFORM_ACCESS_CHECK = false; // TODO: a workaround for v15 + super.setUp(); + } + + @Override + protected String getTestDataPath() { + return "../../SimplePlugin/testData"; + } + + public void testCompletion() { + myFixture.configureByFiles("CompleteTestData.java", "DefaultTestData.simple"); + myFixture.complete(CompletionType.BASIC, 1); + List strings = myFixture.getLookupElementStrings(); + assertTrue(strings.containsAll(Arrays.asList("key with spaces", "language", "message", "tab", "website"))); + assertEquals(5, strings.size()); + } + + public void testAnnotator() { + myFixture.configureByFiles("AnnotatorTestData.java", "DefaultTestData.simple"); + myFixture.checkHighlighting(false, false, true); + } + + public void testFormatter() { + myFixture.configureByFiles("FormatterTestData.simple"); + CodeStyleSettingsManager.getSettings(getProject()).SPACE_AROUND_ASSIGNMENT_OPERATORS = true; + CodeStyleSettingsManager.getSettings(getProject()).KEEP_BLANK_LINES_IN_CODE = 2; + new WriteCommandAction.Simple(getProject()) { + @Override + protected void run() throws Throwable { + CodeStyleManager.getInstance(getProject()).reformat(myFixture.getFile()); + } + }.execute(); + myFixture.checkResultByFile("DefaultTestData.simple"); + } + + public void testRename() { + myFixture.configureByFiles("RenameTestData.java", "RenameTestData.simple"); + myFixture.renameElementAtCaret("websiteUrl"); + myFixture.checkResultByFile("RenameTestData.simple", "RenameTestDataAfter.simple", false); + } + + public void testFolding() { + myFixture.configureByFiles("DefaultTestData.simple"); + myFixture.testFolding(getTestDataPath() + "/FoldingTestData.java"); + } + + public void testFindUsages() { + Collection usageInfos = myFixture.testFindUsages("FindUsagesTestData.simple", "FindUsagesTestData.java"); + assertEquals(1, usageInfos.size()); + } + + public void testCommenter() { + myFixture.configureByText(SimpleFileType.INSTANCE, "website = http://en.wikipedia.org/"); + CommentByLineCommentAction commentAction = new CommentByLineCommentAction(); + commentAction.actionPerformedImpl(getProject(), myFixture.getEditor()); + myFixture.checkResult("#website = http://en.wikipedia.org/"); + commentAction.actionPerformedImpl(getProject(), myFixture.getEditor()); + myFixture.checkResult("website = http://en.wikipedia.org/"); + } + + public void testReference() { + myFixture.configureByFiles("ReferenceTestData.java", "DefaultTestData.simple"); + PsiElement element = myFixture.getFile().findElementAt(myFixture.getCaretOffset()).getParent(); + assertEquals("http://en.wikipedia.org/", ((SimpleProperty) element.getReferences()[0].resolve()).getValue()); + } +} diff --git a/simple_plugin/tests/com/simpleplugin/SimpleParsingTest.java b/simple_plugin/tests/com/simpleplugin/SimpleParsingTest.java new file mode 100644 index 000000000..52848cca2 --- /dev/null +++ b/simple_plugin/tests/com/simpleplugin/SimpleParsingTest.java @@ -0,0 +1,28 @@ +package com.simpleplugin; + +import com.intellij.testFramework.ParsingTestCase; + +public class SimpleParsingTest extends ParsingTestCase { + public SimpleParsingTest() { + super("", "simple", new SimpleParserDefinition()); + } + + public void testParsingTestData() { + doTest(true); + } + + @Override + protected String getTestDataPath() { + return "../../SimplePlugin/testData"; + } + + @Override + protected boolean skipSpaces() { + return false; + } + + @Override + protected boolean includeRanges() { + return true; + } +}