annotateSyntaxErrors(String source, VirtualFile virtualFile) {
+ return Annotator.annotateSyntaxErrors(source, virtualFile);
}
@Override
diff --git a/adapter/src/org/plantuml/idea/adapter/Utils.java b/adapter/src/org/plantuml/idea/adapter/Utils.java
index c0f9c5cb..e3f82cd4 100644
--- a/adapter/src/org/plantuml/idea/adapter/Utils.java
+++ b/adapter/src/org/plantuml/idea/adapter/Utils.java
@@ -4,7 +4,7 @@
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.fileEditor.FileDocumentManager;
import com.intellij.openapi.vfs.VirtualFile;
-import com.intellij.ui.svg.MyTranscoder;
+//import com.intellij.ui.svg.MyTranscoder;
import com.intellij.util.ImageLoader;
import net.sourceforge.plantuml.BlockUml;
import net.sourceforge.plantuml.FileSystem;
@@ -41,18 +41,19 @@ public class Utils {
private static final Logger LOG = Logger.getInstance(Utils.class);
public static BufferedImage loadWithoutCache(@Nullable URL url, @NotNull InputStream stream, double scale, @Nullable ImageLoader.Dimension2DDouble docSize /*OUT*/) {
- try {
- MyTranscoder transcoder = MyTranscoder.createImage(scale, createTranscodeInput(url, stream));
- if (docSize != null) {
- docSize.setSize(transcoder.getOrigDocWidth(), transcoder.getOrigDocHeight());
- }
- return transcoder.getImage();
- } catch (Exception ex) {
- if (docSize != null) {
- docSize.setSize(0, 0);
- }
- throw new RuntimeException(ex);
- }
+// try {
+// MyTranscoder transcoder = MyTranscoder.createImage(scale, createTranscodeInput(url, stream));
+// if (docSize != null) {
+// docSize.setSize(transcoder.getOrigDocWidth(), transcoder.getOrigDocHeight());
+// }
+// return transcoder.getImage();
+// } catch (Exception ex) {
+// if (docSize != null) {
+// docSize.setSize(0, 0);
+// }
+// throw new RuntimeException(ex);
+// }
+ return null;
}
@NotNull
diff --git a/gen/org/plantuml/idea/grammar/PumlLexer.java b/gen/org/plantuml/idea/grammar/PumlLexer.java
new file mode 100644
index 00000000..2e375498
--- /dev/null
+++ b/gen/org/plantuml/idea/grammar/PumlLexer.java
@@ -0,0 +1,583 @@
+/* The following code was generated by JFlex 1.7.0 tweaked for IntelliJ platform */
+
+package org.plantuml.idea.grammar;
+
+import com.intellij.lexer.FlexLexer;
+import com.intellij.psi.TokenType;
+import com.intellij.psi.tree.IElementType;
+import org.plantuml.idea.grammar.psi.PumlTypes;
+
+
+/**
+ * This class is a scanner generated by
+ * JFlex 1.7.0
+ * from the specification file _PumlLexer.flex
+ */
+public class PumlLexer implements FlexLexer {
+
+ /** This character denotes the end of file */
+ public static final int YYEOF = -1;
+
+ /** initial size of the lookahead buffer */
+ private static final int ZZ_BUFFERSIZE = 16384;
+
+ /** lexical states */
+ public static final int YYINITIAL = 0;
+ public static final int LINE_START_STATE = 2;
+ public static final int IN_COMMENT = 4;
+
+ /**
+ * 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, 2, 2
+ };
+
+ /**
+ * Translates characters to character classes
+ * Chosen bits are [7, 7, 7]
+ * Total runtime size is 1928 bytes
+ */
+ public static int ZZ_CMAP(int ch) {
+ return ZZ_CMAP_A[(ZZ_CMAP_Y[ZZ_CMAP_Z[ch >> 14] | ((ch >> 7) & 0x7f)] << 7) | (ch & 0x7f)];
+ }
+
+ /* The ZZ_CMAP_Z table has 68 entries */
+ static final char ZZ_CMAP_Z[] = zzUnpackCMap(
+ "\1\0\103\200");
+
+ /* The ZZ_CMAP_Y table has 256 entries */
+ static final char ZZ_CMAP_Y[] = zzUnpackCMap(
+ "\1\0\1\1\53\2\1\3\22\2\1\4\37\2\1\3\237\2");
+
+ /* The ZZ_CMAP_A table has 640 entries */
+ static final char ZZ_CMAP_A[] = zzUnpackCMap(
+ "\11\0\1\21\1\3\1\20\1\22\1\3\22\0\1\21\1\0\1\12\4\0\1\2\1\7\1\11\2\0\1\10" +
+ "\2\14\1\4\12\15\6\0\1\16\32\17\1\5\1\0\1\6\1\0\1\13\1\0\32\17\12\0\1\20\32" +
+ "\0\1\1\337\0\1\1\177\0\13\1\35\0\2\20\5\0\1\1\57\0\1\1\40\0");
+
+ /**
+ * Translates DFA states to action switch labels.
+ */
+ private static final int[] ZZ_ACTION = zzUnpackAction();
+
+ private static final String ZZ_ACTION_PACKED_0 =
+ "\3\0\1\1\1\2\1\1\1\3\5\1\1\4\1\5" +
+ "\1\6\1\3\1\0\1\7\1\3\1\6\1\3\6\0" +
+ "\1\5\1\10\1\0\1\7";
+
+ private static int[] zzUnpackAction() {
+ int[] result = new int[31];
+ 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\23\0\46\0\71\0\46\0\114\0\137\0\162" +
+ "\0\205\0\230\0\253\0\276\0\321\0\344\0\367\0\u010a" +
+ "\0\u011d\0\u0130\0\u0143\0\u0156\0\u0169\0\u017c\0\u018f\0\u01a2" +
+ "\0\u01b5\0\u01c8\0\321\0\u01db\0\46\0\u01ee\0\46";
+
+ private static int[] zzUnpackRowMap() {
+ int[] result = new int[31];
+ 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\4\1\5\1\6\1\7\1\10\1\11\1\4\1\12" +
+ "\2\4\1\13\1\14\1\4\1\15\1\16\1\15\1\7" +
+ "\1\17\1\20\1\4\1\21\1\22\1\23\1\10\1\11" +
+ "\1\4\1\12\2\4\1\13\1\14\1\4\1\15\1\16" +
+ "\1\15\1\23\1\24\1\25\23\0\1\4\5\0\1\4" +
+ "\1\0\2\4\1\0\2\4\1\0\1\4\4\0\2\26" +
+ "\2\0\17\26\3\0\1\7\14\0\1\7\1\0\1\7" +
+ "\2\0\1\27\20\0\3\30\1\0\2\30\1\0\14\30" +
+ "\3\31\1\0\4\31\2\0\11\31\3\32\1\0\6\32" +
+ "\1\0\10\32\1\4\5\0\1\4\1\0\2\4\1\0" +
+ "\2\14\1\15\1\4\1\15\16\0\2\33\1\15\1\0" +
+ "\1\15\3\0\1\4\5\0\1\4\1\0\2\4\1\0" +
+ "\2\4\1\0\1\4\1\34\24\0\2\17\3\0\1\7" +
+ "\14\0\1\7\1\17\1\20\1\0\1\21\1\22\1\21" +
+ "\14\0\3\21\3\22\1\0\17\22\1\0\1\21\1\22" +
+ "\1\23\14\0\1\23\1\21\1\23\1\0\1\21\1\22" +
+ "\1\21\14\0\1\21\2\24\1\0\1\21\1\22\1\23" +
+ "\14\0\1\23\1\24\1\25\2\26\1\35\1\0\17\26" +
+ "\2\27\1\36\1\27\1\0\16\27\3\30\1\0\2\30" +
+ "\1\35\14\30\3\31\1\0\4\31\1\0\1\35\11\31" +
+ "\3\32\1\0\6\32\1\35\10\32\17\0\1\34\7\0" +
+ "\1\37\16\0";
+
+ private static int[] zzUnpackTrans() {
+ int[] result = new int[513];
+ 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;
+
+ /* error messages for the codes above */
+ private static final String[] ZZ_ERROR_MSG = {
+ "Unknown internal scanner error",
+ "Error: could not match input",
+ "Error: pushback value was too large"
+ };
+
+ /**
+ * ZZ_ATTRIBUTE[aState] contains the attributes of state aState
+ */
+ private static final int[] ZZ_ATTRIBUTE = zzUnpackAttribute();
+
+ private static final String ZZ_ATTRIBUTE_PACKED_0 =
+ "\2\0\1\10\1\1\1\11\13\1\1\0\4\1\6\0" +
+ "\1\1\1\11\1\0\1\11";
+
+ private static int[] zzUnpackAttribute() {
+ int[] result = new int[31];
+ 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 input device
+ */
+ private java.io.Reader zzReader;
+
+ /**
+ * 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 = "";
+
+ /**
+ * the textposition at the last accepting state
+ */
+ private int zzMarkedPos;
+
+ /**
+ * 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;
+
+ /* user code: */
+ public PumlLexer() {
+ this((java.io.Reader) null);
+ }
+
+ private IElementType itemType() {
+ if (org.plantuml.idea.util.Utils.containsLetters(yytext())) {
+ return PumlTypes.IDENTIFIER;
+ } else {
+ return PumlTypes.OTHER;
+ }
+ }
+
+
+ /**
+ * Creates a new scanner
+ *
+ * @param in the java.io.Reader to read input from.
+ */
+ public PumlLexer(java.io.Reader in) {
+ this.zzReader = 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) {
+ int size = 0;
+ for (int i = 0, length = packed.length(); i < length; i += 2) {
+ size += packed.charAt(i);
+ }
+ char[] map = new char[size];
+ int i = 0; /* index in packed string */
+ int j = 0; /* index in unpacked array */
+ while (i < packed.length()) {
+ 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;
+ zzCurrentPos = zzMarkedPos = zzStartRead = start;
+ zzAtEOF = false;
+ zzAtBOL = true;
+ zzEndRead = end;
+ yybegin(initialState);
+ }
+
+ /**
+ * Refills the input buffer.
+ *
+ * @return {@code 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 {@code 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 zzBuffer.charAt(zzStartRead+pos);
+ }
+
+
+ /**
+ * Returns the length of the matched text region.
+ */
+ public final int yylength() {
+ return zzMarkedPos-zzStartRead;
+ }
+
+
+ /**
+ * Reports an error that occurred 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;
+ }
+
+
+ /**
+ * 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;
+
+ 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];
+
+ // set up zzAction for empty match case:
+ int zzAttributes = zzAttrL[zzState];
+ if ((zzAttributes & 1) == 1) {
+ zzAction = zzState;
+ }
+
+
+ zzForAction:
+ {
+ while (true) {
+
+ if (zzCurrentPosL < zzEndReadL) {
+ zzInput = Character.codePointAt(zzBufferL, zzCurrentPosL/*, zzEndReadL*/);
+ zzCurrentPosL += Character.charCount(zzInput);
+ } 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 = Character.codePointAt(zzBufferL, zzCurrentPosL/*, zzEndReadL*/);
+ zzCurrentPosL += Character.charCount(zzInput);
+ }
+ }
+ int zzNext = zzTransL[ zzRowMapL[zzState] + ZZ_CMAP(zzInput)];
+ if (zzNext == -1) break zzForAction;
+ zzState = zzNext;
+
+ zzAttributes = zzAttrL[zzState];
+ if ((zzAttributes & 1) == 1) {
+ zzAction = zzState;
+ zzMarkedPosL = zzCurrentPosL;
+ if ( (zzAttributes & 8) == 8) break zzForAction;
+ }
+
+ }
+ }
+
+ // store back cached position
+ zzMarkedPos = zzMarkedPosL;
+
+ if (zzInput == YYEOF && zzStartRead == zzCurrentPos) {
+ zzAtEOF = true;
+ return null;
+ } else {
+ switch (zzAction < 0 ? zzAction : ZZ_ACTION[zzAction]) {
+ case 1: {
+ yybegin(YYINITIAL);
+ return PumlTypes.OTHER;
+ }
+ // fall through
+ case 9:
+ break;
+ case 2: {
+ return TokenType.BAD_CHARACTER;
+ }
+ // fall through
+ case 10:
+ break;
+ case 3: {
+ yybegin(LINE_START_STATE);
+ return PumlTypes.NEW_LINE_INDENT;
+ }
+ // fall through
+ case 11:
+ break;
+ case 4: {
+ yybegin(YYINITIAL);
+ return itemType();
+ }
+ // fall through
+ case 12:
+ break;
+ case 5: {
+ yybegin(YYINITIAL);
+ return PumlTypes.IDENTIFIER;
+ }
+ // fall through
+ case 13:
+ break;
+ case 6: {
+ yybegin(YYINITIAL);
+ return PumlTypes.WHITE_SPACE;
+ }
+ // fall through
+ case 14:
+ break;
+ case 7: {
+ yybegin(YYINITIAL);
+ return PumlTypes.COMMENT;
+ }
+ // fall through
+ case 15:
+ break;
+ case 8: {
+ yybegin(YYINITIAL);
+ return itemType();
+ }
+ // fall through
+ case 16: break;
+ default:
+ zzScanError(ZZ_NO_MATCH);
+ }
+ }
+ }
+ }
+
+
+}
diff --git a/gen/org/plantuml/idea/grammar/parser/PumlParser.java b/gen/org/plantuml/idea/grammar/parser/PumlParser.java
new file mode 100644
index 00000000..0b0e9154
--- /dev/null
+++ b/gen/org/plantuml/idea/grammar/parser/PumlParser.java
@@ -0,0 +1,74 @@
+// This is a generated file. Not intended for manual editing.
+package org.plantuml.idea.grammar.parser;
+
+import com.intellij.lang.ASTNode;
+import com.intellij.lang.LightPsiParser;
+import com.intellij.lang.PsiBuilder;
+import com.intellij.lang.PsiBuilder.Marker;
+import com.intellij.lang.PsiParser;
+import com.intellij.psi.tree.IElementType;
+
+import static com.intellij.lang.parser.GeneratedParserUtilBase.*;
+import static org.plantuml.idea.grammar.psi.PumlTypes.*;
+
+@SuppressWarnings({"SimplifiableIfStatement", "UnusedAssignment"})
+public class PumlParser 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);
+ r = parse_root_(t, b);
+ exit_section_(b, 0, m, t, r, true, TRUE_CONDITION);
+ }
+
+ protected boolean parse_root_(IElementType t, PsiBuilder b) {
+ return parse_root_(t, b, 0);
+ }
+
+ static boolean parse_root_(IElementType t, PsiBuilder b, int l) {
+ return simpleFile(b, l + 1);
+ }
+
+ /* ********************************************************** */
+ // IDENTIFIER|OTHER
+ public static boolean item(PsiBuilder b, int l) {
+ if (!recursion_guard_(b, l, "item")) return false;
+ if (!nextTokenIs(b, "- ", IDENTIFIER, OTHER)) return false;
+ boolean r;
+ Marker m = enter_section_(b, l, _NONE_, ITEM, "
- ");
+ r = consumeToken(b, IDENTIFIER);
+ if (!r) r = consumeToken(b, OTHER);
+ exit_section_(b, l, m, r, false, null);
+ return r;
+ }
+
+ /* ********************************************************** */
+ // (item|COMMENT|WHITE_SPACE|NEW_LINE_INDENT)*
+ static boolean simpleFile(PsiBuilder b, int l) {
+ if (!recursion_guard_(b, l, "simpleFile")) return false;
+ while (true) {
+ int c = current_position_(b);
+ if (!simpleFile_0(b, l + 1)) break;
+ if (!empty_element_parsed_guard_(b, "simpleFile", c)) break;
+ }
+ return true;
+ }
+
+ // item|COMMENT|WHITE_SPACE|NEW_LINE_INDENT
+ private static boolean simpleFile_0(PsiBuilder b, int l) {
+ if (!recursion_guard_(b, l, "simpleFile_0")) return false;
+ boolean r;
+ r = item(b, l + 1);
+ if (!r) r = consumeToken(b, COMMENT);
+ if (!r) r = consumeToken(b, WHITE_SPACE);
+ if (!r) r = consumeToken(b, NEW_LINE_INDENT);
+ return r;
+ }
+
+}
diff --git a/gen/org/plantuml/idea/grammar/psi/PumlItem.java b/gen/org/plantuml/idea/grammar/psi/PumlItem.java
new file mode 100644
index 00000000..abec6590
--- /dev/null
+++ b/gen/org/plantuml/idea/grammar/psi/PumlItem.java
@@ -0,0 +1,20 @@
+// This is a generated file. Not intended for manual editing.
+package org.plantuml.idea.grammar.psi;
+
+import com.intellij.navigation.ItemPresentation;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiReference;
+
+public interface PumlItem extends PumlNamedElement {
+
+ String getName();
+
+ PsiElement setName(String newName);
+
+ PsiElement getNameIdentifier();
+
+ ItemPresentation getPresentation();
+
+ PsiReference getReference();
+
+}
diff --git a/gen/org/plantuml/idea/grammar/psi/PumlTypes.java b/gen/org/plantuml/idea/grammar/psi/PumlTypes.java
new file mode 100644
index 00000000..983f1ae4
--- /dev/null
+++ b/gen/org/plantuml/idea/grammar/psi/PumlTypes.java
@@ -0,0 +1,28 @@
+// This is a generated file. Not intended for manual editing.
+package org.plantuml.idea.grammar.psi;
+
+import com.intellij.lang.ASTNode;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.tree.IElementType;
+import org.plantuml.idea.grammar.psi.impl.PumlItemImpl;
+
+public interface PumlTypes {
+
+ IElementType ITEM = new PumlElementType("ITEM");
+
+ IElementType COMMENT = new PumlTokenType("COMMENT");
+ IElementType IDENTIFIER = new PumlTokenType("IDENTIFIER");
+ IElementType NEW_LINE_INDENT = new PumlTokenType("NEW_LINE_INDENT");
+ IElementType OTHER = new PumlTokenType("OTHER");
+ IElementType WHITE_SPACE = new PumlTokenType("WHITE_SPACE");
+
+ class Factory {
+ public static PsiElement createElement(ASTNode node) {
+ IElementType type = node.getElementType();
+ if (type == ITEM) {
+ return new PumlItemImpl(node);
+ }
+ throw new AssertionError("Unknown element type: " + type);
+ }
+ }
+}
diff --git a/gen/org/plantuml/idea/grammar/psi/PumlVisitor.java b/gen/org/plantuml/idea/grammar/psi/PumlVisitor.java
new file mode 100644
index 00000000..62fadac2
--- /dev/null
+++ b/gen/org/plantuml/idea/grammar/psi/PumlVisitor.java
@@ -0,0 +1,22 @@
+// This is a generated file. Not intended for manual editing.
+package org.plantuml.idea.grammar.psi;
+
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiElementVisitor;
+import org.jetbrains.annotations.NotNull;
+
+public class PumlVisitor extends PsiElementVisitor {
+
+ public void visitItem(@NotNull PumlItem o) {
+ visitNamedElement(o);
+ }
+
+ public void visitNamedElement(@NotNull PumlNamedElement o) {
+ visitPsiElement(o);
+ }
+
+ public void visitPsiElement(@NotNull PsiElement o) {
+ visitElement(o);
+ }
+
+}
diff --git a/gen/org/plantuml/idea/grammar/psi/impl/PumlItemImpl.java b/gen/org/plantuml/idea/grammar/psi/impl/PumlItemImpl.java
new file mode 100644
index 00000000..99ab2881
--- /dev/null
+++ b/gen/org/plantuml/idea/grammar/psi/impl/PumlItemImpl.java
@@ -0,0 +1,59 @@
+// This is a generated file. Not intended for manual editing.
+package org.plantuml.idea.grammar.psi.impl;
+
+import com.intellij.lang.ASTNode;
+import com.intellij.navigation.ItemPresentation;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiElementVisitor;
+import com.intellij.psi.PsiReference;
+import org.jetbrains.annotations.NotNull;
+import org.plantuml.idea.grammar.psi.PumlItem;
+import org.plantuml.idea.grammar.psi.PumlVisitor;
+
+public class PumlItemImpl extends PumlNamedElementImpl implements PumlItem {
+
+ public PumlItemImpl(@NotNull ASTNode node) {
+ super(node);
+ }
+
+ public void accept(@NotNull PumlVisitor visitor) {
+ visitor.visitItem(this);
+ }
+
+ @Override
+ public void accept(@NotNull PsiElementVisitor visitor) {
+ if (visitor instanceof PumlVisitor) accept((PumlVisitor) visitor);
+ else super.accept(visitor);
+ }
+
+ @Override
+ public String getName() {
+ return PumlPsiImplUtil.getName(this);
+ }
+
+ @Override
+ public PsiElement setName(String newName) {
+ return PumlPsiImplUtil.setName(this, newName);
+ }
+
+ @Override
+ public PsiElement getNameIdentifier() {
+ return PumlPsiImplUtil.getNameIdentifier(this);
+ }
+
+ @Override
+ public ItemPresentation getPresentation() {
+ return PumlPsiImplUtil.getPresentation(this);
+ }
+
+ @Override
+ public PsiReference getReference() {
+ return PumlPsiImplUtil.getReference(this);
+ }
+
+ @Override
+ public String toString() {
+ return PumlPsiImplUtil.toString(this);
+ }
+
+}
diff --git a/plantuml4idea.iml b/plantuml4idea.iml
index 4b13fa91..9c8146bf 100644
--- a/plantuml4idea.iml
+++ b/plantuml4idea.iml
@@ -10,6 +10,7 @@
+
diff --git a/src/fileTemplates/internal/Salt Wireframe.puml.ft b/src/fileTemplates/internal/Salt Wireframe.puml.ft
index 006982a8..f2624d0d 100644
--- a/src/fileTemplates/internal/Salt Wireframe.puml.ft
+++ b/src/fileTemplates/internal/Salt Wireframe.puml.ft
@@ -1,4 +1,6 @@
@startuml
+'https://plantuml.com/salt
+
salt
{
Just plain text
diff --git a/src/fileTemplates/internal/UML Activity.puml.ft b/src/fileTemplates/internal/UML Activity.puml.ft
index cc991d0f..96ada4e6 100644
--- a/src/fileTemplates/internal/UML Activity.puml.ft
+++ b/src/fileTemplates/internal/UML Activity.puml.ft
@@ -1,10 +1,37 @@
@startuml
-(*) --> "check input"
-If "input is verbose" then
---> [Yes] "turn on verbosity"
---> "run command"
+'https://plantuml.com/activity-diagram-beta
+
+start
+:ClickServlet.handleRequest();
+:new page;
+if (Page.onSecurityCheck) then (true)
+ :Page.onInit();
+ if (isForward?) then (no)
+ :Process controls;
+ if (continue processing?) then (no)
+ stop
+ endif
+
+ if (isPost?) then (yes)
+ :Page.onPost();
+ else (no)
+ :Page.onGet();
+ endif
+ :Page.onRender();
+ endif
+else (false)
+endif
+
+if (do redirect?) then (yes)
+ :redirect process;
else
---> "run command"
-Endif
--->(*)
-@enduml
\ No newline at end of file
+ if (do forward?) then (yes)
+ :Forward request;
+ else (no)
+ :Render page template;
+ endif
+endif
+
+stop
+
+@enduml
diff --git a/src/fileTemplates/internal/UML Class.puml.ft b/src/fileTemplates/internal/UML Class.puml.ft
index 993782ad..f2cda622 100644
--- a/src/fileTemplates/internal/UML Class.puml.ft
+++ b/src/fileTemplates/internal/UML Class.puml.ft
@@ -1,4 +1,5 @@
@startuml
+'https://plantuml.com/class-diagram
abstract class AbstractList
abstract AbstractCollection
diff --git a/src/fileTemplates/internal/UML Component.puml.ft b/src/fileTemplates/internal/UML Component.puml.ft
index 6ebfcc45..6bd5ccaa 100644
--- a/src/fileTemplates/internal/UML Component.puml.ft
+++ b/src/fileTemplates/internal/UML Component.puml.ft
@@ -1,14 +1,34 @@
@startuml
+'https://plantuml.com/component-diagram
+
package "Some Group" {
-HTTP - [First Component]
-[Another Component]
+ HTTP - [First Component]
+ [Another Component]
}
-package "Other Groups" {
-FTP - [Second Component]
+node "Other Groups" {
+ FTP - [Second Component]
+ [First Component] --> FTP
+}
-[First Component] --> FTP
+cloud {
+ [Example 1]
}
+
+database "MySql" {
+ folder "This is my folder" {
+ [Folder 3]
+ }
+ frame "Foo" {
+ [Frame 4]
+ }
+}
+
+
+[Another Component] --> [Example 1]
+[Example 1] --> [Folder 3]
+[Folder 3] --> [Frame 4]
+
@enduml
\ No newline at end of file
diff --git a/src/fileTemplates/internal/UML Deployment.puml.ft b/src/fileTemplates/internal/UML Deployment.puml.ft
new file mode 100644
index 00000000..ea4529bf
--- /dev/null
+++ b/src/fileTemplates/internal/UML Deployment.puml.ft
@@ -0,0 +1,30 @@
+@startuml
+'https://plantuml.com/deployment-diagram
+
+actor actor
+actor/ "actor/"
+agent agent
+artifact artifact
+boundary boundary
+card card
+circle circle
+cloud cloud
+collections collections
+component component
+control control
+database database
+entity entity
+file file
+folder folder
+frame frame
+interface interface
+label label
+node node
+package package
+queue queue
+rectangle rectangle
+stack stack
+storage storage
+usecase usecase
+usecase/ "usecase/"
+@enduml
\ No newline at end of file
diff --git a/src/fileTemplates/internal/UML Gantt.puml.ft b/src/fileTemplates/internal/UML Gantt.puml.ft
index 46244d08..b97fe6de 100644
--- a/src/fileTemplates/internal/UML Gantt.puml.ft
+++ b/src/fileTemplates/internal/UML Gantt.puml.ft
@@ -1,4 +1,6 @@
@startgantt
+'https://plantuml.com/gantt-diagram
+
[Prototype design] lasts 13 days and is colored in Lavender/LightBlue
[Test prototype] lasts 9 days and is colored in Coral/Green and starts 3 days after [Prototype design]'s end
[Write tests] lasts 5 days and ends at [Prototype design]'s end
diff --git a/src/fileTemplates/internal/UML MindMap.puml.ft b/src/fileTemplates/internal/UML MindMap.puml.ft
index 9e79a9d4..6aa20be3 100644
--- a/src/fileTemplates/internal/UML MindMap.puml.ft
+++ b/src/fileTemplates/internal/UML MindMap.puml.ft
@@ -1,14 +1,30 @@
@startmindmap
-* Debian
-** Ubuntu
+'https://plantuml.com/mindmap-diagram
+
+caption figure 1
+title My super title
+
+* <&flag>Debian
+** <&globe>Ubuntu
*** Linux Mint
*** Kubuntu
*** Lubuntu
*** KDE Neon
-** LMDE
-** SolydXK
-** SteamOS
-** Raspbian with a very long name
+** <&graph>LMDE
+** <&pulse>SolydXK
+** <&people>SteamOS
+** <&star>Raspbian with a very long name
***
Raspmbc => OSMC
*** Raspyfi => Volumio
-@endmindmap
\ No newline at end of file
+
+header
+My super header
+endheader
+
+center footer My super footer
+
+legend right
+ Short
+ legend
+endlegend
+@endmindmap
diff --git a/src/fileTemplates/internal/UML Object.puml.ft b/src/fileTemplates/internal/UML Object.puml.ft
index 4458d991..bd09ac4a 100644
--- a/src/fileTemplates/internal/UML Object.puml.ft
+++ b/src/fileTemplates/internal/UML Object.puml.ft
@@ -1,15 +1,16 @@
@startuml
-object Object01
-object Object02
-object Object03
-object Object04
-object Object05
-object Object06
-object Object07
-object Object08
+'https://plantuml.com/object-diagram
-Object01 <|-- Object02
-Object03 *-- Object04
-Object05 o-- "4" Object06
-Object07 .. Object08 : some labels
+object London
+object Washington
+object Berlin
+object NewYork
+
+map CapitalCity {
+ UK *-> London
+ USA *--> Washington
+ Germany *---> Berlin
+}
+
+NewYork --> CapitalCity::USA
@enduml
diff --git a/src/fileTemplates/internal/UML Sequence.puml.ft b/src/fileTemplates/internal/UML Sequence.puml.ft
index d0f7d798..ac2c2d39 100644
--- a/src/fileTemplates/internal/UML Sequence.puml.ft
+++ b/src/fileTemplates/internal/UML Sequence.puml.ft
@@ -1,4 +1,8 @@
@startuml
+'https://plantuml.com/sequence-diagram
+
+autonumber
+
Alice -> Bob: Authentication Request
Bob --> Alice: Authentication Response
diff --git a/src/fileTemplates/internal/UML State.puml.ft b/src/fileTemplates/internal/UML State.puml.ft
index 811e8203..88cda1f9 100644
--- a/src/fileTemplates/internal/UML State.puml.ft
+++ b/src/fileTemplates/internal/UML State.puml.ft
@@ -1,4 +1,6 @@
@startuml
+'https://plantuml.com/state-diagram
+
scale 350 width
[*] --> NotShooting
diff --git a/src/fileTemplates/internal/UML Use Case.puml.ft b/src/fileTemplates/internal/UML Use Case.puml.ft
index 80b4f529..fe4b1f34 100644
--- a/src/fileTemplates/internal/UML Use Case.puml.ft
+++ b/src/fileTemplates/internal/UML Use Case.puml.ft
@@ -1,4 +1,6 @@
@startuml
+'https://plantuml.com/use-case-diagram
+
:Main Admin: as Admin
(Use the application) as (Use)
diff --git a/src/fileTemplates/internal/Work Breakdown Structure.puml.ft b/src/fileTemplates/internal/Work Breakdown Structure.puml.ft
index adfa1df9..aad09ff9 100644
--- a/src/fileTemplates/internal/Work Breakdown Structure.puml.ft
+++ b/src/fileTemplates/internal/Work Breakdown Structure.puml.ft
@@ -1,4 +1,6 @@
@startwbs
+'https://plantuml.com/wbs-diagram
+
* Business Process Modelling WBS
** Launch the project
*** Complete Stakeholder Research
diff --git a/src/org/plantuml/idea/external/PlantUmlFacade.java b/src/org/plantuml/idea/external/PlantUmlFacade.java
index 1c50c78d..c043bcc0 100644
--- a/src/org/plantuml/idea/external/PlantUmlFacade.java
+++ b/src/org/plantuml/idea/external/PlantUmlFacade.java
@@ -1,6 +1,6 @@
package org.plantuml.idea.external;
-import com.intellij.psi.PsiFile;
+import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.ImageLoader;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -27,7 +27,7 @@ static PlantUmlFacade getBundled() {
}
@Nullable
- Collection annotateSyntaxErrors(PsiFile file, String source);
+ Collection annotateSyntaxErrors(String source, VirtualFile virtualFile);
void renderAndSave(String source, File sourceFile, PlantUml.ImageFormat format, String path, String pathPrefix, int zoom, int pageNumber)
throws IOException;
diff --git a/src/org/plantuml/idea/grammar/PumlAllItemReference.java b/src/org/plantuml/idea/grammar/PumlAllItemReference.java
new file mode 100644
index 00000000..ce641d57
--- /dev/null
+++ b/src/org/plantuml/idea/grammar/PumlAllItemReference.java
@@ -0,0 +1,41 @@
+package org.plantuml.idea.grammar;
+
+import com.intellij.psi.*;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.plantuml.idea.grammar.psi.PumlItem;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@Deprecated
+public class PumlAllItemReference extends PsiReferenceBase implements PsiPolyVariantReference {
+
+
+ private final String key;
+
+ public PumlAllItemReference(PsiElement element, String text) {
+ super(element);
+ key = text;
+ }
+
+ @NotNull
+ @Override
+ public ResolveResult[] multiResolve(boolean incompleteCode) {
+ List results = new ArrayList<>();
+ List declarationOrUsagesInFile = PumlPsiUtil.findDeclarationInAllFiles(getElement().getProject(), key);
+ for (PumlItem pumlItem : declarationOrUsagesInFile) {
+ results.add(new PsiElementResolveResult(pumlItem));
+ }
+ return results.toArray(new ResolveResult[0]);
+ }
+
+ @Nullable
+ @Override
+ public PsiElement resolve() {
+ ResolveResult[] resolveResults = multiResolve(false);
+ return resolveResults.length == 1 ? resolveResults[0].getElement() : null;
+ }
+
+
+}
diff --git a/src/org/plantuml/idea/grammar/PumlElementManipulator.java b/src/org/plantuml/idea/grammar/PumlElementManipulator.java
new file mode 100644
index 00000000..3d766a09
--- /dev/null
+++ b/src/org/plantuml/idea/grammar/PumlElementManipulator.java
@@ -0,0 +1,18 @@
+package org.plantuml.idea.grammar;
+
+import com.intellij.openapi.util.TextRange;
+import com.intellij.psi.AbstractElementManipulator;
+import com.intellij.psi.PsiElement;
+import com.intellij.util.IncorrectOperationException;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.plantuml.idea.grammar.psi.PumlItem;
+
+public class PumlElementManipulator extends AbstractElementManipulator {
+ @Override
+ public @Nullable
+ PumlItem handleContentChange(@NotNull PumlItem PumlItem, @NotNull TextRange textRange, String s) throws IncorrectOperationException {
+ PsiElement psiElement = PumlItem.setName(s);
+ return (PumlItem) psiElement;
+ }
+}
diff --git a/src/org/plantuml/idea/grammar/PumlItemReference.java b/src/org/plantuml/idea/grammar/PumlItemReference.java
new file mode 100644
index 00000000..05203666
--- /dev/null
+++ b/src/org/plantuml/idea/grammar/PumlItemReference.java
@@ -0,0 +1,65 @@
+package org.plantuml.idea.grammar;
+
+import com.intellij.codeInsight.lookup.LookupElement;
+import com.intellij.codeInsight.lookup.LookupElementBuilder;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiReferenceBase;
+import com.intellij.psi.util.PsiTreeUtil;
+import com.intellij.util.IncorrectOperationException;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.plantuml.idea.grammar.psi.PumlItem;
+import org.plantuml.idea.lang.PlantUmlFileType;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+
+public class PumlItemReference extends PsiReferenceBase {
+
+
+ private final String key;
+
+ public PumlItemReference(PumlItem element, String text) {
+ super(element);
+ key = text;
+ }
+
+ @Nullable
+ @Override
+ public PsiElement resolve() {
+ return PumlPsiUtil.findDeclarationInFile(getElement().getContainingFile(), getElement(), key);
+ }
+
+ @NotNull
+ @Override
+ public Object[] getVariants() {
+ PumlItem[] childrenOfType = PsiTreeUtil.getChildrenOfType(myElement.getContainingFile(), PumlItem.class);
+ HashSet strings = new HashSet<>();
+ List variants = new ArrayList<>();
+
+ if (childrenOfType != null) {
+ for (final PumlItem item : childrenOfType) {
+ String text = item.getText();
+ if (!strings.contains(text)) {
+ strings.add(text);
+ if (text != null && text.length() > 0) {
+ variants.add(LookupElementBuilder
+ .create(item).withIcon(PlantUmlFileType.PLANTUML_ICON)
+ .withTypeText(item.getContainingFile().getName())
+ );
+ }
+ }
+
+ }
+ }
+ return variants.toArray();
+ }
+
+ @Override
+ public PsiElement handleElementRename(@NotNull String newElementName) throws IncorrectOperationException {
+ PumlItem element = getElement();
+ element.setName(newElementName);
+ return element;
+ }
+}
diff --git a/src/org/plantuml/idea/grammar/PumlLexerAdapter.java b/src/org/plantuml/idea/grammar/PumlLexerAdapter.java
new file mode 100644
index 00000000..54a7ae39
--- /dev/null
+++ b/src/org/plantuml/idea/grammar/PumlLexerAdapter.java
@@ -0,0 +1,11 @@
+package org.plantuml.idea.grammar;
+
+import com.intellij.lexer.FlexAdapter;
+
+public class PumlLexerAdapter extends FlexAdapter {
+
+ public PumlLexerAdapter() {
+ super(new PumlLexer(null));
+ }
+
+}
diff --git a/src/org/plantuml/idea/grammar/PumlNamesValidator.java b/src/org/plantuml/idea/grammar/PumlNamesValidator.java
new file mode 100644
index 00000000..c0529dce
--- /dev/null
+++ b/src/org/plantuml/idea/grammar/PumlNamesValidator.java
@@ -0,0 +1,21 @@
+package org.plantuml.idea.grammar;
+
+import com.intellij.lang.refactoring.NamesValidator;
+import com.intellij.openapi.project.Project;
+import org.jetbrains.annotations.NotNull;
+
+import static org.apache.commons.lang.StringUtils.containsNone;
+
+public class PumlNamesValidator implements NamesValidator {
+ @Override
+ public boolean isKeyword(@NotNull String name, Project project) {
+ return false;
+ }
+
+ @Override
+ public boolean isIdentifier(@NotNull String name, Project project) {
+ return (!name.isEmpty() && containsNone(name, new char[]{' ', '\t'}))
+ || (name.startsWith("[") && name.endsWith("]"))
+ || (name.startsWith("(") && name.endsWith(")"));
+ }
+}
\ No newline at end of file
diff --git a/src/org/plantuml/idea/grammar/PumlPsiUtil.java b/src/org/plantuml/idea/grammar/PumlPsiUtil.java
new file mode 100644
index 00000000..1b1135a9
--- /dev/null
+++ b/src/org/plantuml/idea/grammar/PumlPsiUtil.java
@@ -0,0 +1,136 @@
+package org.plantuml.idea.grammar;
+
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.module.ModuleUtilCore;
+import com.intellij.openapi.project.DumbService;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.psi.PsiClass;
+import com.intellij.psi.PsiFile;
+import com.intellij.psi.PsiManager;
+import com.intellij.psi.impl.java.stubs.index.JavaShortClassNameIndex;
+import com.intellij.psi.search.FileTypeIndex;
+import com.intellij.psi.search.GlobalSearchScope;
+import com.intellij.psi.util.PsiTreeUtil;
+import org.jetbrains.annotations.NotNull;
+import org.plantuml.idea.grammar.psi.PumlItem;
+import org.plantuml.idea.lang.PlantUmlFileType;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class PumlPsiUtil {
+
+ public static List findDeclarationOrUsagesInFile(PsiFile containingFile, @NotNull PumlItem element, String key) {
+ List result = new ArrayList<>();
+ PumlItem[] items = PsiTreeUtil.getChildrenOfType(containingFile, PumlItem.class);
+ boolean returnFirst = true;
+ boolean firstMatch = true;
+
+ if (items != null) {
+ for (PumlItem item : items) {
+ if (isSame(key, item.getText())) {
+ if (firstMatch && item == element) {
+ returnFirst = false;
+ continue;
+ }
+
+ result.add(item);
+ if (returnFirst) {
+ break;
+ }
+ firstMatch = false;
+ }
+ }
+ }
+ return result;
+ }
+
+ public static PumlItem findDeclarationInFile(PsiFile containingFile, @NotNull PumlItem element, String key) {
+ PumlItem[] items = PsiTreeUtil.getChildrenOfType(containingFile, PumlItem.class);
+ if (items != null) {
+ for (PumlItem item : items) {
+ if (item == element) {
+ return null;
+ }
+ if (isSame(key, item.getText())) {
+ return item;
+ }
+ }
+ }
+ return null;
+ }
+
+ static final Pattern SANITIZER = Pattern.compile("([a-zA-Z0-9].*[a-zA-Z0-9])");
+
+ public static boolean isSame(String p1, String p2) {
+ if (p1.equals(p2)) {
+ return true;
+ }
+ Matcher m = SANITIZER.matcher(p1);
+ String key = null;
+ if (m.find()) {
+ key = m.group();
+ }
+ if (key == null) {
+ return false;
+ }
+
+ String key2 = null;
+ m.reset(p2);
+ if (m.find()) {
+ key2 = m.group();
+ }
+ return key.equals(key2);
+ }
+
+ public static List findDeclarationInAllFiles(Project project, String key) {
+ List result = new ArrayList<>();
+ Collection virtualFiles = FileTypeIndex.getFiles(PlantUmlFileType.INSTANCE, GlobalSearchScope.allScope(project));
+ for (VirtualFile virtualFile : virtualFiles) {
+ PsiFile simpleFile = (PsiFile) PsiManager.getInstance(project).findFile(virtualFile);
+ if (simpleFile != null) {
+ PumlItem[] properties = PsiTreeUtil.getChildrenOfType(simpleFile, PumlItem.class);
+ if (properties != null) {
+ for (PumlItem property : properties) {
+ if (isSame(key, property.getText())) {
+ result.add(property);
+ break;
+ }
+ }
+ }
+ }
+ }
+ return result;
+ }
+
+ public static List findAll(PsiFile file) {
+ List result = new ArrayList<>();
+ if (file != null) {
+ PumlItem[] items = PsiTreeUtil.getChildrenOfType(file, PumlItem.class);
+ if (items != null) {
+ Collections.addAll(result, items);
+ }
+ }
+ return result;
+ }
+
+ public static Collection findJavaClass(PumlItem element) {
+ if (DumbService.isDumb(element.getProject())) {
+ return Collections.emptyList();
+ }
+ String text = element.getText();
+
+ Module moduleForFile = ModuleUtilCore.findModuleForFile(element.getContainingFile());
+ if (moduleForFile != null) {
+ GlobalSearchScope scope = GlobalSearchScope.moduleScope(moduleForFile);
+ return JavaShortClassNameIndex.getInstance().get(text, element.getProject(), scope);
+ } else {
+ return JavaShortClassNameIndex.getInstance().get(text, element.getProject(), GlobalSearchScope.allScope(element.getProject()));
+ }
+ }
+}
diff --git a/src/org/plantuml/idea/grammar/PumlReferenceContributor.java b/src/org/plantuml/idea/grammar/PumlReferenceContributor.java
new file mode 100644
index 00000000..aad31687
--- /dev/null
+++ b/src/org/plantuml/idea/grammar/PumlReferenceContributor.java
@@ -0,0 +1,30 @@
+
+package org.plantuml.idea.grammar;
+
+import com.intellij.patterns.PlatformPatterns;
+import com.intellij.psi.*;
+import com.intellij.util.ProcessingContext;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * can navigate from java string to puml
+ */
+@Deprecated
+public class PumlReferenceContributor extends PsiReferenceContributor {
+ public PumlReferenceContributor() {
+ }
+
+ @Override
+ public void registerReferenceProviders(@NotNull PsiReferenceRegistrar registrar) {
+ registrar.registerReferenceProvider(PlatformPatterns.psiElement(PsiElement.class),
+ new PsiReferenceProvider() {
+ @NotNull
+ @Override
+ public PsiReference[] getReferencesByElement(@NotNull PsiElement element,
+ @NotNull ProcessingContext context) {
+ return new PsiReference[]{new PumlAllItemReference(element, element.getText())};
+ }
+ });
+ }
+
+}
diff --git a/src/org/plantuml/idea/grammar/SimpleFindUsagesProvider.java b/src/org/plantuml/idea/grammar/SimpleFindUsagesProvider.java
new file mode 100644
index 00000000..e216c058
--- /dev/null
+++ b/src/org/plantuml/idea/grammar/SimpleFindUsagesProvider.java
@@ -0,0 +1,65 @@
+package org.plantuml.idea.grammar;
+
+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.tree.TokenSet;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.plantuml.idea.grammar.psi.PumlItem;
+import org.plantuml.idea.grammar.psi.PumlTypes;
+
+public class SimpleFindUsagesProvider implements FindUsagesProvider {
+
+ @Nullable
+ @Override
+ public WordsScanner getWordsScanner() {
+ return new DefaultWordsScanner(new PumlLexerAdapter(),
+ TokenSet.create(PumlTypes.IDENTIFIER),
+ TokenSet.create(PumlTypes.COMMENT),
+ TokenSet.EMPTY);
+ }
+
+ @Override
+ public boolean canFindUsagesFor(@NotNull PsiElement psiElement) {
+ return psiElement instanceof PumlItem;
+ }
+
+ @Nullable
+ @Override
+ public String getHelpId(@NotNull PsiElement psiElement) {
+ return null;
+ }
+
+ @NotNull
+ @Override
+ public String getType(@NotNull PsiElement element) {
+ if (element instanceof PumlItem) {
+ return "PumlItem";
+ } else {
+ return "";
+ }
+ }
+
+ @NotNull
+ @Override
+ public String getDescriptiveName(@NotNull PsiElement element) {
+ if (element instanceof PumlItem) {
+ return element.getText();
+ } else {
+ return "";
+ }
+ }
+
+ @NotNull
+ @Override
+ public String getNodeText(@NotNull PsiElement element, boolean useFullName) {
+ if (element instanceof PumlItem) {
+ return element.getText();
+ } else {
+ return "";
+ }
+ }
+
+}
diff --git a/src/org/plantuml/idea/grammar/_Puml.bnf b/src/org/plantuml/idea/grammar/_Puml.bnf
new file mode 100644
index 00000000..18398b57
--- /dev/null
+++ b/src/org/plantuml/idea/grammar/_Puml.bnf
@@ -0,0 +1,25 @@
+{
+ parserClass="org.plantuml.idea.grammar.parser.PumlParser"
+// parserUtilClass="org.plantuml.idea.language.parser.PumlParserUtil"
+
+ extends="com.intellij.extapi.psi.ASTWrapperPsiElement"
+
+ psiClassPrefix="Puml"
+ psiImplClassSuffix="Impl"
+ psiPackage="org.plantuml.idea.grammar.psi"
+ psiImplPackage="org.plantuml.idea.grammar.psi.impl"
+
+ elementTypeHolderClass="org.plantuml.idea.grammar.psi.PumlTypes"
+ elementTypeClass="org.plantuml.idea.grammar.psi.PumlElementType"
+ tokenTypeClass="org.plantuml.idea.grammar.psi.PumlTokenType"
+
+ psiImplUtilClass="org.plantuml.idea.grammar.psi.impl.PumlPsiImplUtil"
+}
+
+simpleFile ::= (item|COMMENT|WHITE_SPACE|NEW_LINE_INDENT)*
+
+item ::= IDENTIFIER|OTHER {
+ mixin="org.plantuml.idea.grammar.psi.impl.PumlNamedElementImpl"
+ implements="org.plantuml.idea.grammar.psi.PumlNamedElement"
+ methods=[ getName setName getNameIdentifier getPresentation getReference toString]
+}
diff --git a/src/org/plantuml/idea/grammar/_PumlLexer.flex b/src/org/plantuml/idea/grammar/_PumlLexer.flex
new file mode 100644
index 00000000..f0ac6f0f
--- /dev/null
+++ b/src/org/plantuml/idea/grammar/_PumlLexer.flex
@@ -0,0 +1,70 @@
+package org.plantuml.idea.grammar;
+
+import com.intellij.lexer.FlexLexer;
+import com.intellij.psi.TokenType;
+import com.intellij.psi.tree.IElementType;
+import org.plantuml.idea.grammar.psi.PumlTypes;
+
+%%
+
+%{
+ public PumlLexer() {
+ this((java.io.Reader)null);
+ }
+
+ private IElementType itemType() {
+ if(org.plantuml.idea.util.Utils.containsLetters(yytext())) {
+ return PumlTypes.IDENTIFIER;
+ } else {
+ return PumlTypes.OTHER;
+ }
+ }
+%}
+
+%public
+%class PumlLexer
+%implements FlexLexer
+%function advance
+%type IElementType
+%unicode
+
+LINE_COMMENT=\s*'[^\r\n]*
+BLOCK_COMMENT="/'"[^'/]*"'/"
+
+BRACKET_1=\[[^\]\r\n]+\] // [foo bar]
+BRACKET_2=\([^\)\r\n,]+\) //without ',' -> do not eat multiple items: (ProductOfferingPrice, ProductUsageSpec)
+
+QUOTE_1=\"[^\"\r\n]+\" // "foo bar"
+QUOTE_2=\'[^\'\r\n]+\' // 'foo bar'
+
+COMPLEX_WORD=[A-Za-z0-9_][A-Za-z0-9._-]*[A-Za-z0-9]
+WORD_CHARACTER=[A-Za-z0-9]
+TAG=@[A-Za-z]*
+SPECIAL_CHARACTER=[^A-Za-z0-9\s/\[\(\"'] //except quotes, brackets start
+NEW_LINE=\R
+WHITE_SPACE=[\ \t\f]
+
+%xstate LINE_START_STATE,IN_COMMENT
+
+%%
+
+
+{LINE_COMMENT} { yybegin(YYINITIAL); return PumlTypes.COMMENT; }
+{BLOCK_COMMENT} { yybegin(YYINITIAL); return PumlTypes.COMMENT; }
+{BRACKET_1} { yybegin(YYINITIAL); return itemType(); }
+{BRACKET_2} { yybegin(YYINITIAL); return itemType(); }
+{QUOTE_1} { yybegin(YYINITIAL); return itemType(); }
+{QUOTE_2} { yybegin(YYINITIAL); return itemType(); }
+{COMPLEX_WORD} { yybegin(YYINITIAL); return itemType(); }
+{WORD_CHARACTER}+ { yybegin(YYINITIAL); return itemType(); }
+{TAG} { yybegin(YYINITIAL); return PumlTypes.IDENTIFIER; }
+{SPECIAL_CHARACTER}+ { yybegin(YYINITIAL); return PumlTypes.OTHER; }
+"/" { yybegin(YYINITIAL); return PumlTypes.OTHER; }
+"[" { yybegin(YYINITIAL); return PumlTypes.OTHER; }
+"(" { yybegin(YYINITIAL); return PumlTypes.OTHER; }
+"\"" { yybegin(YYINITIAL); return PumlTypes.OTHER; }
+"'" { yybegin(YYINITIAL); return PumlTypes.OTHER; }
+{NEW_LINE}+ { yybegin(LINE_START_STATE); return PumlTypes.NEW_LINE_INDENT; }
+{WHITE_SPACE}+ { yybegin(YYINITIAL); return PumlTypes.WHITE_SPACE; }
+
+[^] { return TokenType.BAD_CHARACTER; }
diff --git a/src/org/plantuml/idea/grammar/psi/PumlElementFactory.java b/src/org/plantuml/idea/grammar/psi/PumlElementFactory.java
new file mode 100644
index 00000000..f76ecf6a
--- /dev/null
+++ b/src/org/plantuml/idea/grammar/psi/PumlElementFactory.java
@@ -0,0 +1,26 @@
+package org.plantuml.idea.grammar.psi;
+
+import com.intellij.openapi.project.Project;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiFileFactory;
+import org.plantuml.idea.lang.PlantUmlFileImpl;
+import org.plantuml.idea.lang.PlantUmlFileType;
+
+public class PumlElementFactory {
+
+ public static PumlItem createWord(Project project, String name) {
+ final PlantUmlFileImpl file = createFile(project, name);
+ return (PumlItem) file.getFirstChild();
+ }
+
+ public static PlantUmlFileImpl createFile(Project project, String text) {
+ String name = "dummy.puml";
+ return (PlantUmlFileImpl) PsiFileFactory.getInstance(project).createFileFromText(name, PlantUmlFileType.INSTANCE, text);
+ }
+
+ public static PsiElement createCRLF(Project project) {
+ final PlantUmlFileImpl file = createFile(project, "\n");
+ return file.getFirstChild();
+ }
+
+}
diff --git a/src/org/plantuml/idea/grammar/psi/PumlElementType.java b/src/org/plantuml/idea/grammar/psi/PumlElementType.java
new file mode 100644
index 00000000..997d754a
--- /dev/null
+++ b/src/org/plantuml/idea/grammar/psi/PumlElementType.java
@@ -0,0 +1,14 @@
+package org.plantuml.idea.grammar.psi;
+
+import com.intellij.psi.tree.IElementType;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
+import org.plantuml.idea.lang.PlantUmlLanguage;
+
+public class PumlElementType extends IElementType {
+
+ public PumlElementType(@NotNull @NonNls String debugName) {
+ super(debugName, PlantUmlLanguage.INSTANCE);
+ }
+
+}
diff --git a/src/org/plantuml/idea/grammar/psi/PumlNamedElement.java b/src/org/plantuml/idea/grammar/psi/PumlNamedElement.java
new file mode 100644
index 00000000..eb584e45
--- /dev/null
+++ b/src/org/plantuml/idea/grammar/psi/PumlNamedElement.java
@@ -0,0 +1,7 @@
+package org.plantuml.idea.grammar.psi;
+
+import com.intellij.psi.PsiNameIdentifierOwner;
+
+public interface PumlNamedElement extends PsiNameIdentifierOwner {
+
+}
diff --git a/src/org/plantuml/idea/grammar/psi/PumlTokenType.java b/src/org/plantuml/idea/grammar/psi/PumlTokenType.java
new file mode 100644
index 00000000..16e6892f
--- /dev/null
+++ b/src/org/plantuml/idea/grammar/psi/PumlTokenType.java
@@ -0,0 +1,19 @@
+package org.plantuml.idea.grammar.psi;
+
+import com.intellij.psi.tree.IElementType;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
+import org.plantuml.idea.lang.PlantUmlLanguage;
+
+public class PumlTokenType extends IElementType {
+
+ public PumlTokenType(@NotNull @NonNls String debugName) {
+ super(debugName, PlantUmlLanguage.INSTANCE);
+ }
+
+ @Override
+ public String toString() {
+ return "PumlTokenType." + super.toString();
+ }
+
+}
diff --git a/src/org/plantuml/idea/grammar/psi/impl/PumlNamedElementImpl.java b/src/org/plantuml/idea/grammar/psi/impl/PumlNamedElementImpl.java
new file mode 100644
index 00000000..730cda6a
--- /dev/null
+++ b/src/org/plantuml/idea/grammar/psi/impl/PumlNamedElementImpl.java
@@ -0,0 +1,14 @@
+package org.plantuml.idea.grammar.psi.impl;
+
+import com.intellij.extapi.psi.ASTWrapperPsiElement;
+import com.intellij.lang.ASTNode;
+import org.jetbrains.annotations.NotNull;
+import org.plantuml.idea.grammar.psi.PumlNamedElement;
+
+public abstract class PumlNamedElementImpl extends ASTWrapperPsiElement implements PumlNamedElement {
+
+ public PumlNamedElementImpl(@NotNull ASTNode node) {
+ super(node);
+ }
+
+}
diff --git a/src/org/plantuml/idea/grammar/psi/impl/PumlPsiImplUtil.java b/src/org/plantuml/idea/grammar/psi/impl/PumlPsiImplUtil.java
new file mode 100644
index 00000000..b7f690bd
--- /dev/null
+++ b/src/org/plantuml/idea/grammar/psi/impl/PumlPsiImplUtil.java
@@ -0,0 +1,97 @@
+package org.plantuml.idea.grammar.psi.impl;
+
+import com.intellij.lang.ASTNode;
+import com.intellij.navigation.ItemPresentation;
+import com.intellij.openapi.editor.Document;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiFile;
+import com.intellij.psi.PsiReference;
+import org.jetbrains.annotations.Nullable;
+import org.plantuml.idea.grammar.PumlItemReference;
+import org.plantuml.idea.grammar.psi.PumlElementFactory;
+import org.plantuml.idea.grammar.psi.PumlItem;
+import org.plantuml.idea.lang.PlantUmlFileType;
+
+import javax.swing.*;
+
+public class PumlPsiImplUtil {
+ public static String getName(PumlItem element) {
+ return element.getText();
+ }
+
+ public static PsiElement getNameIdentifier(PumlItem element) {
+ return (PsiElement) element.getNode().getFirstChildNode();
+ }
+
+ public static PsiElement setName(PumlItem element, String newName) {
+ ASTNode keyNode = element.getNode().getFirstChildNode();
+ if (keyNode != null) {
+ PumlItem property = PumlElementFactory.createWord(element.getProject(), newName);
+ ASTNode newKeyNode = property.getFirstChild().getNode();
+ element.getNode().replaceChild(keyNode, newKeyNode);
+ }
+ return element;
+ }
+
+
+ public static ItemPresentation getPresentation(final PumlItem element) {
+ return new ItemPresentation() {
+ @Nullable
+ @Override
+ public String getPresentableText() {
+ return element.getText();
+ }
+
+ @Nullable
+ @Override
+ public String getLocationString() {
+ PsiFile containingFile = element.getContainingFile();
+ return containingFile == null ? null : containingFile.getName();
+ }
+
+ @Override
+ public Icon getIcon(boolean unused) {
+ return PlantUmlFileType.PLANTUML_ICON;
+ }
+ };
+ }
+
+ public static ItemPresentation getPresentation2(final PumlItem element, Document document) {
+ return new ItemPresentation() {
+ @Nullable
+ @Override
+ public String getPresentableText() {
+ return element.getText();
+ }
+
+ @Nullable
+ @Override
+ public String getLocationString() {
+ if (document == null) {
+ return null;
+ }
+ int lineNumber = document.getLineNumber(element.getTextOffset());
+ return "line: " + lineNumber;
+ }
+
+ @Override
+ public Icon getIcon(boolean unused) {
+ return PlantUmlFileType.PLANTUML_ICON;
+ }
+ };
+ }
+
+ public static PsiReference getReference(PumlItem element) {
+ return new PumlItemReference(element, element.getText());
+ }
+
+ @Deprecated
+ public static PsiReference[] getReferences(PumlItem element) {
+ return new PsiReference[]{getReference(element)};
+ }
+
+ public static String toString(PumlItem element) {
+ return element.getClass().getSimpleName() + "(" + element.getText() + ")" + element.getTextRange();
+ }
+
+}
diff --git a/src/org/plantuml/idea/grammar/structure/PumlStructureViewElement.java b/src/org/plantuml/idea/grammar/structure/PumlStructureViewElement.java
new file mode 100644
index 00000000..4d5afe71
--- /dev/null
+++ b/src/org/plantuml/idea/grammar/structure/PumlStructureViewElement.java
@@ -0,0 +1,98 @@
+package org.plantuml.idea.grammar.structure;
+
+import com.intellij.ide.projectView.PresentationData;
+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.openapi.editor.Document;
+import com.intellij.psi.NavigatablePsiElement;
+import com.intellij.psi.PsiDocumentManager;
+import com.intellij.psi.util.PsiTreeUtil;
+import org.jetbrains.annotations.NotNull;
+import org.plantuml.idea.grammar.psi.PumlItem;
+import org.plantuml.idea.grammar.psi.PumlTypes;
+import org.plantuml.idea.grammar.psi.impl.PumlItemImpl;
+import org.plantuml.idea.grammar.psi.impl.PumlPsiImplUtil;
+import org.plantuml.idea.lang.PlantUmlFileImpl;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+
+public class PumlStructureViewElement implements StructureViewTreeElement, SortableTreeElement {
+
+ private final NavigatablePsiElement myElement;
+ private final Document document;
+
+ public PumlStructureViewElement(NavigatablePsiElement element, Document document) {
+ this.myElement = element;
+ this.document = document;
+ }
+
+ @Override
+ public Object getValue() {
+ return myElement;
+ }
+
+ @Override
+ public void navigate(boolean requestFocus) {
+ myElement.navigate(requestFocus);
+ }
+
+ @Override
+ public boolean canNavigate() {
+ return myElement.canNavigate();
+ }
+
+ @Override
+ public boolean canNavigateToSource() {
+ return myElement.canNavigateToSource();
+ }
+
+ @NotNull
+ @Override
+ public String getAlphaSortKey() {
+ String name = myElement.getName();
+ return name != null ? name : "";
+ }
+
+ @NotNull
+ @Override
+ public ItemPresentation getPresentation() {
+ if (myElement instanceof PumlItem) {
+ return PumlPsiImplUtil.getPresentation2((PumlItem) myElement, document);
+ }
+ ItemPresentation presentation = myElement.getPresentation();
+ return presentation != null ? presentation : new PresentationData();
+ }
+
+ @NotNull
+ @Override
+ public TreeElement[] getChildren() {
+ Document document = PsiDocumentManager.getInstance(myElement.getProject()).getDocument(myElement.getContainingFile());
+ if (myElement instanceof PlantUmlFileImpl) {
+ List treeElements = new ArrayList<>();
+ List list = PsiTreeUtil.getChildrenOfTypeAsList(myElement, PumlItemImpl.class);
+
+ HashSet strings = new HashSet<>();
+ for (final PumlItemImpl item : list) {
+ if (item.getFirstChild().getNode().getElementType() != PumlTypes.IDENTIFIER) {
+ continue;
+ }
+ String text = item.getText();
+ if (!strings.contains(text)) {
+ strings.add(text);
+ if (text != null && text.length() > 0) {
+ treeElements.add(new PumlStructureViewElement(item, document));
+ }
+ }
+ }
+
+
+ return treeElements.toArray(new TreeElement[0]);
+ }
+ return EMPTY_ARRAY;
+ }
+
+}
diff --git a/src/org/plantuml/idea/grammar/structure/PumlStructureViewFactory.java b/src/org/plantuml/idea/grammar/structure/PumlStructureViewFactory.java
new file mode 100644
index 00000000..bf1a5d22
--- /dev/null
+++ b/src/org/plantuml/idea/grammar/structure/PumlStructureViewFactory.java
@@ -0,0 +1,29 @@
+package org.plantuml.idea.grammar.structure;
+
+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.Document;
+import com.intellij.openapi.editor.Editor;
+import com.intellij.psi.PsiDocumentManager;
+import com.intellij.psi.PsiFile;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+public class PumlStructureViewFactory implements PsiStructureViewFactory {
+
+ @Nullable
+ @Override
+ public StructureViewBuilder getStructureViewBuilder(@NotNull final PsiFile psiFile) {
+ return new TreeBasedStructureViewBuilder() {
+ @NotNull
+ @Override
+ public StructureViewModel createStructureViewModel(@Nullable Editor editor) {
+ Document document = PsiDocumentManager.getInstance(psiFile.getProject()).getDocument(psiFile.getContainingFile());
+ return new PumlStructureViewModel(psiFile, document);
+ }
+ };
+ }
+
+}
diff --git a/src/org/plantuml/idea/grammar/structure/PumlStructureViewModel.java b/src/org/plantuml/idea/grammar/structure/PumlStructureViewModel.java
new file mode 100644
index 00000000..62967a30
--- /dev/null
+++ b/src/org/plantuml/idea/grammar/structure/PumlStructureViewModel.java
@@ -0,0 +1,35 @@
+package org.plantuml.idea.grammar.structure;
+
+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.openapi.editor.Document;
+import com.intellij.psi.PsiFile;
+import org.jetbrains.annotations.NotNull;
+import org.plantuml.idea.lang.PlantUmlFileImpl;
+
+public class PumlStructureViewModel extends StructureViewModelBase implements
+ StructureViewModel.ElementInfoProvider {
+
+ public PumlStructureViewModel(PsiFile psiFile, Document document) {
+ super(psiFile, new PumlStructureViewElement(psiFile, document));
+ }
+
+ @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 PlantUmlFileImpl;
+ }
+
+}
diff --git a/src/org/plantuml/idea/intentions/AddPartialRenderOptionIntention.java b/src/org/plantuml/idea/intentions/AddPartialRenderOptionIntention.java
index 8609283c..3127c225 100644
--- a/src/org/plantuml/idea/intentions/AddPartialRenderOptionIntention.java
+++ b/src/org/plantuml/idea/intentions/AddPartialRenderOptionIntention.java
@@ -37,7 +37,7 @@ public String getText() {
@Override
public boolean isAvailable(@NotNull Project project, final Editor editor, PsiFile file) {
- if (!file.getFileType().equals(PlantUmlFileType.PLANTUML_FILE_TYPE)) return false;
+ if (!file.getFileType().equals(PlantUmlFileType.INSTANCE)) return false;
int offset = editor.getCaretModel().getOffset();
return new AddPartialRenderCommand(editor, offset).isAvailable();
}
diff --git a/src/org/plantuml/idea/lang/PlantUmlCompletionContributor.java b/src/org/plantuml/idea/lang/PlantUmlCompletionContributor.java
index 09189b42..4933cf9c 100644
--- a/src/org/plantuml/idea/lang/PlantUmlCompletionContributor.java
+++ b/src/org/plantuml/idea/lang/PlantUmlCompletionContributor.java
@@ -30,7 +30,7 @@ public PlantUmlCompletionContributor() {
extend(
CompletionType.BASIC,
PlatformPatterns.psiElement(),
- new SimpleProvider(LanguageDescriptor.INSTANCE.keywordsWithoutHighlight));
+ new SimpleProvider(LanguageDescriptor.INSTANCE.keywords2));
}
@@ -54,7 +54,8 @@ public void fillCompletionVariants(@NotNull CompletionParameters parameters, @No
return;
}
super.fillCompletionVariants(parameters, result);
- if (PlantUmlSettings.getInstance().isAutoComplete()) {
+ PlantUmlSettings settings = PlantUmlSettings.getInstance();
+ if (settings.isAutoComplete() && !settings.isUseGrammar()) { //PumlItemReference.getVariants duplicates it
WordCompletionContributor.addWordCompletionVariants(result, parameters, Collections.emptySet());
}
}
diff --git a/src/org/plantuml/idea/lang/PlantUmlFileImpl.java b/src/org/plantuml/idea/lang/PlantUmlFileImpl.java
index 66b339ab..2535fbde 100644
--- a/src/org/plantuml/idea/lang/PlantUmlFileImpl.java
+++ b/src/org/plantuml/idea/lang/PlantUmlFileImpl.java
@@ -17,6 +17,6 @@ public PlantUmlFileImpl(FileViewProvider viewProvider) {
@NotNull
@Override
public FileType getFileType() {
- return PlantUmlFileType.PLANTUML_FILE_TYPE;
+ return PlantUmlFileType.INSTANCE;
}
}
diff --git a/src/org/plantuml/idea/lang/PlantUmlFileType.java b/src/org/plantuml/idea/lang/PlantUmlFileType.java
index c0873419..e94772bd 100644
--- a/src/org/plantuml/idea/lang/PlantUmlFileType.java
+++ b/src/org/plantuml/idea/lang/PlantUmlFileType.java
@@ -9,7 +9,7 @@
public class PlantUmlFileType extends LanguageFileType {
- public static final PlantUmlFileType PLANTUML_FILE_TYPE = new PlantUmlFileType();
+ public static final PlantUmlFileType INSTANCE = new PlantUmlFileType();
public static final String PLANTUML_EXT = "puml";
public static final String PLANTUML_EXT_2 = "plantuml";
diff --git a/src/org/plantuml/idea/lang/PlantUmlParserDefinition.java b/src/org/plantuml/idea/lang/PlantUmlParserDefinition.java
index 55500716..fc0eb37a 100644
--- a/src/org/plantuml/idea/lang/PlantUmlParserDefinition.java
+++ b/src/org/plantuml/idea/lang/PlantUmlParserDefinition.java
@@ -7,19 +7,27 @@
import com.intellij.lexer.EmptyLexer;
import com.intellij.lexer.Lexer;
import com.intellij.openapi.project.Project;
-import com.intellij.psi.FileViewProvider;
-import com.intellij.psi.PlainTextTokenTypes;
-import com.intellij.psi.PsiElement;
-import com.intellij.psi.PsiFile;
+import com.intellij.psi.*;
import com.intellij.psi.tree.IFileElementType;
import com.intellij.psi.tree.TokenSet;
import com.intellij.psi.util.PsiUtilCore;
import org.jetbrains.annotations.NotNull;
+import org.plantuml.idea.grammar.PumlLexerAdapter;
+import org.plantuml.idea.grammar.parser.PumlParser;
+import org.plantuml.idea.grammar.psi.PumlTypes;
+import org.plantuml.idea.lang.settings.PlantUmlSettings;
/**
* @author Eugene Steinberg
*/
public class PlantUmlParserDefinition implements ParserDefinition {
+
+ public static final TokenSet WHITE_SPACES = TokenSet.create(TokenType.WHITE_SPACE);
+ public static final TokenSet COMMENTS = TokenSet.create(PumlTypes.COMMENT);
+
+ public static final IFileElementType FILE = new IFileElementType(PlantUmlLanguage.INSTANCE);
+
+
private static final IFileElementType PLANTUML_FILE_ELEMENT_TYPE = new IFileElementType(PlantUmlLanguage.INSTANCE) {
@Override
public ASTNode parseContents(ASTNode chameleon) {
@@ -27,34 +35,52 @@ public ASTNode parseContents(ASTNode chameleon) {
return ASTFactory.leaf(PlainTextTokenTypes.PLAIN_TEXT, chars);
}
};
+ private PlantUmlSettings plantUmlSettings;
+
+ public PlantUmlParserDefinition() {
+ plantUmlSettings = PlantUmlSettings.getInstance();
+ }
@Override
@NotNull
public Lexer createLexer(Project project) {
- return new EmptyLexer();
+ if (enabled()) {
+ return new PumlLexerAdapter();
+ } else {
+ return new EmptyLexer();
+ }
}
+
@Override
@NotNull
public PsiParser createParser(Project project) {
- throw new UnsupportedOperationException("Not supported");
+ if (enabled()) {
+ return new PumlParser();
+ } else {
+ throw new UnsupportedOperationException("Not supported");
+ }
}
@Override
public IFileElementType getFileNodeType() {
- return PLANTUML_FILE_ELEMENT_TYPE;
+ if (enabled()) {
+ return FILE;
+ } else {
+ return PLANTUML_FILE_ELEMENT_TYPE;
+ }
}
@Override
@NotNull
public TokenSet getWhitespaceTokens() {
- return TokenSet.EMPTY;
+ return WHITE_SPACES;
}
@Override
@NotNull
public TokenSet getCommentTokens() {
- return TokenSet.EMPTY;
+ return COMMENTS;
}
@Override
@@ -63,10 +89,14 @@ public TokenSet getStringLiteralElements() {
return TokenSet.EMPTY;
}
- @Override
@NotNull
+ @Override
public PsiElement createElement(ASTNode node) {
- return PsiUtilCore.NULL_PSI_ELEMENT;
+ if (enabled()) {
+ return PumlTypes.Factory.createElement(node);
+ } else {
+ return PsiUtilCore.NULL_PSI_ELEMENT;
+ }
}
@Override
@@ -78,4 +108,8 @@ public PsiFile createFile(FileViewProvider viewProvider) {
public SpaceRequirements spaceExistenceTypeBetweenTokens(ASTNode left, ASTNode right) {
return SpaceRequirements.MAY;
}
+
+ private boolean enabled() {
+ return plantUmlSettings.isUseGrammar();
+ }
}
diff --git a/src/org/plantuml/idea/lang/PlantUmlTypeFactory.java b/src/org/plantuml/idea/lang/PlantUmlTypeFactory.java
index 51a07b27..2cd0784e 100644
--- a/src/org/plantuml/idea/lang/PlantUmlTypeFactory.java
+++ b/src/org/plantuml/idea/lang/PlantUmlTypeFactory.java
@@ -12,10 +12,10 @@ public class PlantUmlTypeFactory extends FileTypeFactory {
@Override
public void createFileTypes(@NotNull FileTypeConsumer consumer) {
consumer.consume(
- PlantUmlFileType.PLANTUML_FILE_TYPE,
+ PlantUmlFileType.INSTANCE,
PlantUmlFileType.PLANTUML_EXT);
consumer.consume(
- PlantUmlFileType.PLANTUML_FILE_TYPE,
+ PlantUmlFileType.INSTANCE,
PlantUmlFileType.PLANTUML_EXT_2);
consumer.consume(
PlantIUmlFileType.PLANTUML_FILE_TYPE,
diff --git a/src/org/plantuml/idea/lang/annotator/LanguageDescriptor.java b/src/org/plantuml/idea/lang/annotator/LanguageDescriptor.java
index 539ece98..c342bfdd 100644
--- a/src/org/plantuml/idea/lang/annotator/LanguageDescriptor.java
+++ b/src/org/plantuml/idea/lang/annotator/LanguageDescriptor.java
@@ -79,7 +79,7 @@ public enum LanguageDescriptor {
));
- public final List keywordsWithoutHighlight = Collections.unmodifiableList(Arrays.asList(
+ public final List keywords2 = Collections.unmodifiableList(Arrays.asList(
"as",
"also",
"of",
diff --git a/src/org/plantuml/idea/lang/annotator/LanguagePatternHolder.java b/src/org/plantuml/idea/lang/annotator/LanguagePatternHolder.java
index 13ba562d..42703988 100644
--- a/src/org/plantuml/idea/lang/annotator/LanguagePatternHolder.java
+++ b/src/org/plantuml/idea/lang/annotator/LanguagePatternHolder.java
@@ -24,6 +24,7 @@ public enum LanguagePatternHolder {
public final Pattern pluginSettingsPattern = createPattern(LanguageDescriptor.INSTANCE.pluginSettingsPattern, "");
public final Pattern keywordsPattern = createPattern(LanguageDescriptor.INSTANCE.keywords, "");
+ public final Pattern keywords2Pattern = createPattern(LanguageDescriptor.INSTANCE.keywords2, "");
public final Pattern typesPattern = createPattern(LanguageDescriptor.INSTANCE.types, "");
public final Pattern preprocPattern = createPattern2(LanguageDescriptor.INSTANCE.preproc, "");
public final Pattern tagsPattern = Pattern.compile("@(start|end)(" + addWordStop(TAGS) + ")");
diff --git a/src/org/plantuml/idea/lang/annotator/PlantUmlExternalAnnotator.java b/src/org/plantuml/idea/lang/annotator/PlantUmlExternalAnnotator.java
index ee8fade8..0aeffe08 100644
--- a/src/org/plantuml/idea/lang/annotator/PlantUmlExternalAnnotator.java
+++ b/src/org/plantuml/idea/lang/annotator/PlantUmlExternalAnnotator.java
@@ -2,13 +2,17 @@
import com.intellij.lang.annotation.AnnotationHolder;
import com.intellij.lang.annotation.ExternalAnnotator;
+import com.intellij.openapi.application.Application;
+import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.DefaultLanguageHighlighterColors;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.colors.TextAttributesKey;
+import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiDocumentManager;
import com.intellij.psi.PsiFile;
import org.apache.commons.lang.StringUtils;
+import org.apache.commons.lang.math.IntRange;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.plantuml.idea.external.PlantUmlFacade;
@@ -21,39 +25,67 @@
import java.util.regex.Matcher;
import static com.intellij.openapi.editor.DefaultLanguageHighlighterColors.METADATA;
+import static org.plantuml.idea.util.Utils.join;
+import static org.plantuml.idea.util.Utils.rangesInside;
/**
* Author: Eugene Steinberg
* Date: 9/13/14
*/
-public class PlantUmlExternalAnnotator extends ExternalAnnotator {
+public class PlantUmlExternalAnnotator extends ExternalAnnotator {
private static final Logger logger = Logger.getInstance(PlantUmlExternalAnnotator.class);
+ private PlantUmlSettings plantUmlSettings;
+
+ public PlantUmlExternalAnnotator() {
+ plantUmlSettings = PlantUmlSettings.getInstance();
+ }
+
+ public static class Info {
+
+ private final String text;
+ private final VirtualFile virtualFile;
+
+ public Info(String text, VirtualFile virtualFile) {
+ this.text = text;
+ this.virtualFile = virtualFile;
+ }
+
+ public Info(PsiFile file) {
+ this(file.getText(), file.getVirtualFile());
+ }
+ }
@Nullable
@Override
- public PsiFile collectInformation(@NotNull PsiFile file) {
- return file;
+ public Info collectInformation(@NotNull PsiFile file) {
+ return new Info(file.getText(), file.getVirtualFile());
}
@Nullable
@Override
- public FileAnnotationResult doAnnotate(PsiFile file) {
+ public FileAnnotationResult doAnnotate(Info file) {
+ // Temporary solution to avoid execution under read action in dumb mode. Should be removed after IDEA-229905 will be fixed
+ Application application = ApplicationManager.getApplication();
+ if (application != null && application.isReadAccessAllowed() && !application.isUnitTestMode()) {
+ return null;
+ }
+
FileAnnotationResult result = new FileAnnotationResult();
- if (PlantUmlSettings.getInstance().isErrorAnnotationEnabled()) {
- String text = file.getFirstChild().getText();
+ if (plantUmlSettings.isErrorAnnotationEnabled() || plantUmlSettings.isKeywordHighlighting()) {
+ String text = file.text;
Map sources = PlantUml.extractSources(text);
for (Map.Entry sourceData : sources.entrySet()) {
Integer sourceOffset = sourceData.getKey();
-
SourceAnnotationResult sourceAnnotationResult = new SourceAnnotationResult(sourceOffset);
-
String source = sourceData.getValue();
- sourceAnnotationResult.addAll(PlantUmlFacade.get().annotateSyntaxErrors(file, source));
- List blockComments = annotateBlockComments(source);
- sourceAnnotationResult.addBlockComments(blockComments);
+ if (plantUmlSettings.isErrorAnnotationEnabled()) {
+ sourceAnnotationResult.addAll(PlantUmlFacade.get().annotateSyntaxErrors(source, file.virtualFile));
+ List blockComments = annotateBlockComments(source);
+ sourceAnnotationResult.addBlockComments(blockComments);
+ }
annotateByLine(sourceAnnotationResult, source);
@@ -65,38 +97,72 @@ public FileAnnotationResult doAnnotate(PsiFile file) {
public void annotateByLine(SourceAnnotationResult result, String source) {
Matcher keywords = LanguagePatternHolder.INSTANCE.keywordsPattern.matcher("");
- Matcher pluginSettings = LanguagePatternHolder.INSTANCE.pluginSettingsPattern.matcher("");
+ Matcher keywords2 = LanguagePatternHolder.INSTANCE.keywords2Pattern.matcher("");
Matcher types = LanguagePatternHolder.INSTANCE.typesPattern.matcher("");
+ Matcher pluginSettings = LanguagePatternHolder.INSTANCE.pluginSettingsPattern.matcher("");
Matcher preproc = LanguagePatternHolder.INSTANCE.preprocPattern.matcher("");
Matcher tags = LanguagePatternHolder.INSTANCE.tagsPattern.matcher("");
Matcher commentMatcher = LanguagePatternHolder.INSTANCE.lineCommentPattern.matcher("");
String[] strings = StringUtils.splitPreserveAllTokens(source, "\n");
- int i = 0;
+ int offset = 0;
for (String line : strings) {
commentMatcher.reset(line);
if (commentMatcher.find()) {
- result.addWithBlockCommentCheck(new SyntaxHighlightAnnotation(i + commentMatcher.start(), i + commentMatcher.end(), DefaultLanguageHighlighterColors.LINE_COMMENT));
+ SyntaxHighlightAnnotation lineComment = new SyntaxHighlightAnnotation(offset + commentMatcher.start(), offset + commentMatcher.end(), DefaultLanguageHighlighterColors.LINE_COMMENT);
+ result.addWithBlockCommentCheck(lineComment);
} else {
- annotate(result, line, i, DefaultLanguageHighlighterColors.KEYWORD, keywords);
- annotate(result, line, i, DefaultLanguageHighlighterColors.KEYWORD, pluginSettings);
- annotate(result, line, i, DefaultLanguageHighlighterColors.LABEL, types);
- annotate(result, line, i, DefaultLanguageHighlighterColors.METADATA, preproc);
- annotate(result, line, i, METADATA, tags);
+ if (plantUmlSettings.isKeywordHighlighting()) {
+ highlightKeywords(result, keywords, types, keywords2, offset, line);
+ }
+ annotate(result, line, offset, null, DefaultLanguageHighlighterColors.KEYWORD, pluginSettings);
+ annotate(result, line, offset, null, DefaultLanguageHighlighterColors.METADATA, preproc);
+ annotate(result, line, offset, null, METADATA, tags);
}
- i += line.length() + 1;
+ offset += line.length() + 1;
}
}
- private void annotate(SourceAnnotationResult result, String line, int i, TextAttributesKey textAttributesKey, Matcher matcher) {
+ private void highlightKeywords(SourceAnnotationResult result, Matcher keywords, Matcher types, Matcher keywords2, int offset, String line) {
+ int i = line.indexOf(":"); //it seems no keywords are after :
+ if (i > 0) {
+ line = line.substring(0, i);
+ }
+ //not reliable when mixed braces ([)...(]), but it don't need to be
+ List excludedRanges = join(rangesInside(line, "[", "]"), rangesInside(line, "(", ")"));
+ excludedRanges = join(excludedRanges, rangesInside(line, "\"", "\""));
+
+ annotate(result, line, offset, excludedRanges, DefaultLanguageHighlighterColors.KEYWORD, keywords);
+ annotate(result, line, offset, excludedRanges, DefaultLanguageHighlighterColors.KEYWORD, keywords2);
+ annotate(result, line, offset, excludedRanges, DefaultLanguageHighlighterColors.KEYWORD, types);
+ }
+
+ private void annotate(SourceAnnotationResult result, String line, int offset, List excludedRanges, TextAttributesKey textAttributesKey, Matcher matcher) {
matcher.reset(line);
+
while (matcher.find()) {
- result.addWithBlockCommentCheck(new SyntaxHighlightAnnotation(i + matcher.start(), i + matcher.end(), textAttributesKey));
+ if (isExcluded(excludedRanges, matcher.start())) {
+ continue;
+ }
+ SyntaxHighlightAnnotation a = new SyntaxHighlightAnnotation(offset + matcher.start(), offset + matcher.end(), textAttributesKey);
+ result.addWithBlockCommentCheck(a);
+ }
+ }
+
+ private boolean isExcluded(List excludedRanges, int start) {
+ if (excludedRanges != null) {
+ for (IntRange excludedRange : excludedRanges) {
+ if (excludedRange.containsInteger(start)) {
+ return true;
+ }
+ }
}
+ return false;
}
+
private List annotateBlockComments(String source) {
List result = new ArrayList<>();
diff --git a/src/org/plantuml/idea/lang/annotator/SourceAnnotationResult.java b/src/org/plantuml/idea/lang/annotator/SourceAnnotationResult.java
index b07c293e..a6cce2ac 100644
--- a/src/org/plantuml/idea/lang/annotator/SourceAnnotationResult.java
+++ b/src/org/plantuml/idea/lang/annotator/SourceAnnotationResult.java
@@ -42,11 +42,13 @@ public void addWithBlockCommentCheck(SyntaxHighlightAnnotation syntaxHighlightAn
}
private boolean inBlockComment(SyntaxHighlightAnnotation syntaxHighlightAnnotation) {
- for (int i = 0, blockCommentsSize = blockComments.size(); i < blockCommentsSize; i++) {
- SyntaxHighlightAnnotation blockComment = blockComments.get(i);
- int startSourceOffset = syntaxHighlightAnnotation.startSourceOffset;
- if (blockComment.getStartSourceOffset() < startSourceOffset && startSourceOffset < blockComment.getEndSourceOffset()) {
- return true;
+ if (blockComments != null) {
+ for (int i = 0, blockCommentsSize = blockComments.size(); i < blockCommentsSize; i++) {
+ SyntaxHighlightAnnotation blockComment = blockComments.get(i);
+ int startSourceOffset = syntaxHighlightAnnotation.startSourceOffset;
+ if (blockComment.getStartSourceOffset() < startSourceOffset && startSourceOffset < blockComment.getEndSourceOffset()) {
+ return true;
+ }
}
}
return false;
diff --git a/src/org/plantuml/idea/lang/settings/PlantUmlSettings.java b/src/org/plantuml/idea/lang/settings/PlantUmlSettings.java
index 736570d5..a6df5a89 100644
--- a/src/org/plantuml/idea/lang/settings/PlantUmlSettings.java
+++ b/src/org/plantuml/idea/lang/settings/PlantUmlSettings.java
@@ -62,6 +62,8 @@ public class PlantUmlSettings implements PersistentStateComponent Notifications.Bus.notify(NOTIFICATION.createNotification("Switching to a bundled PlantUML v" + version, MessageType.INFO)));
}
@@ -300,4 +302,20 @@ public boolean isUsePageTitles() {
public void setUsePageTitles(final boolean usePageTitles) {
this.usePageTitles = usePageTitles;
}
+
+ public boolean isUseGrammar() {
+ return useGrammar;
+ }
+
+ public void setUseGrammar(final boolean useGrammar) {
+ this.useGrammar = useGrammar;
+ }
+
+ public boolean isKeywordHighlighting() {
+ return keywordHighlighting;
+ }
+
+ public void setKeywordHighlighting(final boolean keywordHighlighting) {
+ this.keywordHighlighting = keywordHighlighting;
+ }
}
diff --git a/src/org/plantuml/idea/lang/settings/PlantUmlSettingsPage.form b/src/org/plantuml/idea/lang/settings/PlantUmlSettingsPage.form
index dd2ca588..e8edd334 100644
--- a/src/org/plantuml/idea/lang/settings/PlantUmlSettingsPage.form
+++ b/src/org/plantuml/idea/lang/settings/PlantUmlSettingsPage.form
@@ -1,19 +1,19 @@