diff --git a/META-INF/plugin.xml b/META-INF/plugin.xml index e91a989d..75af6363 100644 --- a/META-INF/plugin.xml +++ b/META-INF/plugin.xml @@ -1,6 +1,6 @@ PlantUML integration - 3.1.0 + 3.2.0 Eugene Steinberg @@ -9,6 +9,13 @@ 3.2

+
    +
  • Added: ctrl+click - Go to Declaration/Usages, Rename...
  • +
  • Added: Structure view - shows the first occurrence of a word
  • +
  • Better keyword highlighting + settings to disable it
  • +
  • Updated file templates
  • +

3.1

  • Performance optimization when !includeurl is used
  • @@ -23,192 +30,6 @@
  • Option to name diagram files based on their title instead of page number (enabled by default)
  • Moved settings under "Languages and Frameworks".
-

2.29

-
    -
  • Settings migration from CFG to XML
  • -
  • PlantUml library upgrade to v1.2020.23
  • -
  • JSON Diagram support
  • -
-

2.28

-
    -
  • PlantUml library upgrade to v1.2020.20
  • -
  • zoom fix
  • -
-

2.27

-
    -
  • PlantUml library upgrade to v1.2020.19
  • -
  • Fixed %filename() and %dirpath()
  • -
  • Latex support (#265)
  • -
-

2.26

-
    -
  • New option to not display errors
  • -
  • Left click on the status label toggles between an error and a last non-error diagram
  • -
  • Markdown support
  • -
-

2.25

-
    -
  • PlantUml library upgrade to v1.2020.9
  • -
  • Fixed scaling on multiple monitors with different DPI
  • -
  • Setting for PLANTUML_LIMIT_SIZE
  • -
  • Setting for plantuml.include.path
  • -
-

2.24

-
    -
  • PlantUml library upgrade to v1.2020.7
  • -
  • Render URL links fix
  • -
  • Remember file extension in export dialog
  • -
-

2.23

-
    -
  • PlantUml library upgrade to a new beta - to fix !include
  • -
-

2.22

-
    -
  • NPE fix
  • -
  • PlantUml library upgrade to v1.2019.11
  • -
-

2.21

-
    -
  • Fixed '*' stripping
  • -
-

2.20

-
    -
  • PlantUml library upgrade to v1.2019.10
  • -
-

2.19

-
    -
  • PlantUml library upgrade to v1.2019.8
  • -
  • Zooming fix
  • -
  • "IllegalStateException: Timeout4 TIMEOUT" while zooming fixed
  • -
-

2.18

-
    -
  • PlantUml library upgrade to v1.2019.7
  • -
  • Fixed icons for IntelliJ 2019.2
  • -
-

2.17

-
    -
  • Export diagram fix
  • -
-

2.16

-
    -
  • Exception fix
  • -
-

2.15

-
    -
  • PlantUml library upgrade to v1.2019.5
  • -
  • Caching edge case fixed
  • -
  • Autocomplete improvements
  • -
-

2.14

-
    -
  • PlantUml library upgrade to v1.2019.4
  • -
  • Automatic code completion popup
  • -
  • Fixed reverse arrow intention
  • -
  • Added settings for encoding and PlantUML config (global include)
  • -
  • Using the filename from "@startuml name" when exporting
  • -
  • Added highlighting of comments
  • -
  • Updated highlighting of tags and keywords
  • -
  • Fixed copy to clipboard
  • -
  • reStructuredText files support
  • -
-

2.13

-
    -
  • PlantUml library upgrade to v1.2019.3
  • -
  • Fixed image scaling in Windows 10
  • -
  • Added MindMap support
  • -
  • Added Gantt support
  • -
  • Added Work Breakdown Structure support
  • -
  • Added option to use GRAPHVIZ_DOT environment variable preferentially
  • -
  • Grouped file templates
  • -
  • Restoring scroll position after an error
  • -
  • Zooming even when scale is defined in the code
  • -
-

2.12

-
    -
  • PlantUml library upgrade to v1.2018.11
  • -
  • Fixed diagram saving
  • -
-

2.11

-
    -
  • PlantUml library upgrade to v1.2018.9
  • -
-

2.10

-
    -
  • PlantUml library upgrade to v1.2018.1
  • -
-

2.9

-
    -
  • PlantUml library upgrade to v1.2017.15
  • -
  • Exception fixes
  • -
-

2.8

-
    -
  • PlantUml library upgrade to v1.2017.12
  • -
-

2.7

-
    -
  • Fixed opening in an external editor for multiple-page diagrams
  • -
  • Fixed edge cases for Copy and Save diagram actions
  • -
-

2.6

-
    -
  • NPE fix for AndroidStudio
  • -
  • PlantUml library upgrade to v2017.08
  • -
  • Added support for *.plantuml file extension
  • -
-

2.5

-
    -
  • Disabled asserts in PlantUml - they were causing slow rendering
  • -
  • PlantUml library upgrade to 8053
  • -
-

2.4

-
    -
  • PlantUml library upgrade to 8051
  • -
  • Changed default shortcuts to Ctrl+Alt+Shift+F and G
  • -
  • NPE fix
  • -
-

2.3.1

-
    -
  • Fixed ASCII image rendering and typos
  • -
-

2.3

-
    -
  • Path to Graphviz dot executable was ignored
  • -
  • PlantUml library upgrade to 8043
  • -
  • Added a low memory watcher for cache clearing
  • -
-

2.2

-
    -
  • Toggle button for disabling automatic rendering
  • -
  • When auto-rendering is disabled, update and reload button will be red
  • -
  • ALT+D shortcut for diagram update
  • -
  • ALT+F shortcut for diagram reload
  • -
  • Page titles fixed for partial rendering
  • -
  • Usage manual
  • -
  • About dialog reworked
  • -
  • IJ 12 compatibility fix
  • -
-

2.1

-
    -
  • Scrolling fixed for 2016.2
  • -
  • Remembering selected page per file
  • -
  • New right click action: Generate PlantUML Server link to clipboard
  • -
  • Page titles in the dropdown page selector
  • -
-

2.0

-
    -
  • PlantUml library upgrade to 8042
  • -
  • Images can be copied to clipboard as ASCII Art
  • -
  • Fixed: @startditaa` not recognized
  • -
  • Added a configurable delay between key up event and rendering run
  • -
  • Disabled URL links rendering by default - doubles rendering time
  • -
  • A lot of caching and optimizations
  • -
  • Added incremental rendering - only changed pages will be rendered
  • -
  • Added partial rendering mode - useful in large files - the document will be splitted by (@)newpage and each piece will be rendered by itself.
  • -
  • Added intentions to disable syntax check, and enable partial rendering - available on the start tag (@startuml,...)
  • -
]]>
com.intellij.modules.platform @@ -332,5 +153,14 @@ implementationClass="org.plantuml.idea.lang.PlantUmlCompletionContributor"/> + + + + + +
diff --git a/adapter/src/org/plantuml/idea/adapter/Annotator.java b/adapter/src/org/plantuml/idea/adapter/Annotator.java index 926b3487..8d9f1246 100644 --- a/adapter/src/org/plantuml/idea/adapter/Annotator.java +++ b/adapter/src/org/plantuml/idea/adapter/Annotator.java @@ -2,7 +2,6 @@ import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.psi.PsiFile; import net.sourceforge.plantuml.syntax.SyntaxChecker; import net.sourceforge.plantuml.syntax.SyntaxResult; import org.apache.commons.lang.StringUtils; @@ -24,13 +23,13 @@ public class Annotator { private static final Logger logger = Logger.getInstance(Annotator.class); @Nullable - public static Collection annotateSyntaxErrors(PsiFile file, String source) { + public static Collection annotateSyntaxErrors(String source, VirtualFile virtualFile) { if (source.contains(IDEA_DISABLE_SYNTAX_CHECK)) { return Collections.emptyList(); } Collection result = new ArrayList(); long start = currentTimeMillis(); - SyntaxResult syntaxResult = checkSyntax(file, source); + SyntaxResult syntaxResult = checkSyntax(source, virtualFile); logger.debug("syntax checked in ", currentTimeMillis() - start, "ms"); if (syntaxResult.isError()) { @@ -49,12 +48,11 @@ public static Collection annotateSyntaxErrors(PsiFile file, St return result; } - private static SyntaxResult checkSyntax(PsiFile file, String source) { - File baseDir = UIUtils.getParent(file.getVirtualFile()); + private static SyntaxResult checkSyntax(String source, VirtualFile virtualFile) { + File baseDir = UIUtils.getParent(virtualFile); if (baseDir != null) { Utils.setPlantUmlDir(baseDir); - VirtualFile virtualFile = file.getVirtualFile(); Utils.saveAllDocuments(virtualFile==null?null:virtualFile.getPath()); } else { Utils.resetPlantUmlDir(); diff --git a/adapter/src/org/plantuml/idea/adapter/FacadeImpl.java b/adapter/src/org/plantuml/idea/adapter/FacadeImpl.java index 8c012e14..1e2f5edf 100644 --- a/adapter/src/org/plantuml/idea/adapter/FacadeImpl.java +++ b/adapter/src/org/plantuml/idea/adapter/FacadeImpl.java @@ -1,7 +1,7 @@ package org.plantuml.idea.adapter; import com.intellij.openapi.diagnostic.Logger; -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; @@ -29,8 +29,8 @@ public class FacadeImpl implements PlantUmlFacade { @Nullable @Override - public Collection annotateSyntaxErrors(PsiFile file, String source) { - return Annotator.annotateSyntaxErrors(file, source); + public Collection 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 @@

- + - + - + - + @@ -21,7 +21,7 @@ - + @@ -29,7 +29,7 @@ - + @@ -37,7 +37,7 @@ - + @@ -45,7 +45,7 @@ - + @@ -53,7 +53,7 @@ - + @@ -61,7 +61,7 @@ - + @@ -69,10 +69,10 @@ - + - + @@ -109,7 +109,7 @@ - + @@ -126,12 +126,12 @@ - + - + @@ -139,7 +139,7 @@ - + @@ -149,13 +149,13 @@ - + - + @@ -163,7 +163,7 @@ - + @@ -180,26 +180,26 @@ - + - + - + - + @@ -233,7 +233,7 @@ - + @@ -275,7 +275,7 @@ - + @@ -309,7 +309,7 @@ - + @@ -369,12 +369,34 @@ - + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/org/plantuml/idea/lang/settings/PlantUmlSettingsPage.java b/src/org/plantuml/idea/lang/settings/PlantUmlSettingsPage.java index 2e2cb04a..fe22ec17 100644 --- a/src/org/plantuml/idea/lang/settings/PlantUmlSettingsPage.java +++ b/src/org/plantuml/idea/lang/settings/PlantUmlSettingsPage.java @@ -48,6 +48,8 @@ public class PlantUmlSettingsPage implements Configurable { private JLabel version; private JButton web; private JCheckBox usePageTitles; + private JCheckBox grammarSupport; + private JCheckBox keywordHighlighting; public PlantUmlSettingsPage() { browse.addActionListener(new ActionListener() { @@ -177,6 +179,8 @@ public void setData(PlantUmlSettings data) { switchToBundledAfterUpdate.setSelected(data.isSwitchToBundledAfterUpdate()); customPlantumlJar.setText(data.getCustomPlantumlJarPath()); usePageTitles.setSelected(data.isUsePageTitles()); + grammarSupport.setSelected(data.isUseGrammar()); + keywordHighlighting.setSelected(data.isKeywordHighlighting()); } public void getData(PlantUmlSettings data) { @@ -194,6 +198,8 @@ public void getData(PlantUmlSettings data) { data.setSwitchToBundledAfterUpdate(switchToBundledAfterUpdate.isSelected()); data.setCustomPlantumlJarPath(customPlantumlJar.getText()); data.setUsePageTitles(usePageTitles.isSelected()); + data.setUseGrammar(grammarSupport.isSelected()); + data.setKeywordHighlighting(keywordHighlighting.isSelected()); } public boolean isModified(PlantUmlSettings data) { @@ -219,6 +225,8 @@ public boolean isModified(PlantUmlSettings data) { if (customPlantumlJar.getText() != null ? !customPlantumlJar.getText().equals(data.getCustomPlantumlJarPath()) : data.getCustomPlantumlJarPath() != null) return true; if (usePageTitles.isSelected() != data.isUsePageTitles()) return true; + if (grammarSupport.isSelected() != data.isUseGrammar()) return true; + if (keywordHighlighting.isSelected() != data.isKeywordHighlighting()) return true; return false; } } diff --git a/src/org/plantuml/idea/plantuml/CreatePlantUMLFileAction.java b/src/org/plantuml/idea/plantuml/CreatePlantUMLFileAction.java index 1de059b3..7193e1ad 100644 --- a/src/org/plantuml/idea/plantuml/CreatePlantUMLFileAction.java +++ b/src/org/plantuml/idea/plantuml/CreatePlantUMLFileAction.java @@ -31,6 +31,7 @@ protected void buildDialog(Project project, PsiDirectory directory, CreateFileFr .addKind("Component", PlantUmlFileType.PLANTUML_ICON, "UML Component") .addKind("State", PlantUmlFileType.PLANTUML_ICON, "UML State") .addKind("Object", PlantUmlFileType.PLANTUML_ICON, "UML Object") + .addKind("Deployment", PlantUmlFileType.PLANTUML_ICON, "UML Deployment") .addKind("Gantt", PlantUmlFileType.PLANTUML_ICON, "UML Gantt") .addKind("MindMap", PlantUmlFileType.PLANTUML_ICON, "UML MindMap") .addKind("Wireframe", PlantUmlFileType.PLANTUML_ICON, "Salt Wireframe") diff --git a/src/org/plantuml/idea/toolwindow/PlantUmlImagePanelSvg.java b/src/org/plantuml/idea/toolwindow/PlantUmlImagePanelSvg.java index 4db24547..6f6809f5 100644 --- a/src/org/plantuml/idea/toolwindow/PlantUmlImagePanelSvg.java +++ b/src/org/plantuml/idea/toolwindow/PlantUmlImagePanelSvg.java @@ -1,263 +1,263 @@ -package org.plantuml.idea.toolwindow; - -import com.intellij.openapi.Disposable; -import com.intellij.openapi.actionSystem.*; -import com.intellij.openapi.diagnostic.Logger; -import com.intellij.ui.ColoredSideBorder; -import com.intellij.ui.JBColor; -import com.intellij.ui.PopupHandler; -import com.intellij.ui.components.JBLayeredPane; -import com.intellij.ui.components.Magnificator; -import org.intellij.images.editor.ImageDocument; -import org.intellij.images.ui.ImageComponent; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.plantuml.idea.action.context.*; -import org.plantuml.idea.lang.settings.PlantUmlSettings; -import org.plantuml.idea.rendering.ImageItem; -import org.plantuml.idea.rendering.RenderRequest; - -import javax.swing.*; -import javax.swing.event.ChangeEvent; -import javax.swing.event.ChangeListener; -import java.awt.*; -import java.awt.event.MouseAdapter; -import java.awt.event.MouseEvent; -import java.awt.image.BufferedImage; -import java.io.IOException; -import java.net.URI; - -/** - * TODO CopyDiagramToClipboardContextAction not working - */ -public class PlantUmlImagePanelSvg extends JPanel implements Disposable { - private static final AnAction[] AN_ACTIONS = { - new SaveDiagramToFileContextAction(), - new CopyDiagramToClipboardContextAction(), - Separator.getInstance(), - new CopyDiagramAsTxtToClipboardContextAction(), - new CopyDiagramAsUnicodeTxtToClipboardContextAction(), - Separator.getInstance(), - new CopyDiagramAsLatexToClipboardContextAction(), - new CopyDiagramAsTikzCodeToClipboardContextAction(), - Separator.getInstance(), - new ExternalOpenDiagramAsPNGAction(), - new ExternalOpenDiagramAsSVGAction(), - Separator.getInstance(), - new CopyPlantUmlServerLinkContextAction() - }; - private static final ActionPopupMenu ACTION_POPUP_MENU = ActionManager.getInstance().createActionPopupMenu(ActionPlaces.UNKNOWN, new ActionGroup() { - - @NotNull - @Override - public AnAction[] getChildren(@Nullable AnActionEvent e) { - return AN_ACTIONS; - } - }); - - private static Logger logger = Logger.getInstance(PlantUmlImagePanelSvg.class); - private RenderRequest renderRequest; - private ImageItem imageWithData; - private Image originalImage; - private ImageComponent imageComponent; - - public PlantUmlImagePanelSvg(ImageItem imageWithData, int i, RenderRequest renderRequest) { - this.imageWithData = imageWithData; - setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); - - setup(this.imageWithData, i, renderRequest); - } - - public ImageItem getImageWithData() { - return imageWithData; - } - - public int getPage() { - return imageWithData.getPage(); - } - - public RenderRequest getRenderRequest() { - return renderRequest; - } - - public void setup(@NotNull ImageItem imageWithData, int i, RenderRequest renderRequest) { - setOpaque(true); - setBackground(JBColor.WHITE); - if (imageWithData.hasImage()) { - setDiagram(imageWithData); - } else { - add(new JLabel("page not rendered, probably plugin error, please report it and try to hit reload")); - } - this.renderRequest = renderRequest; - } - - /** - * Scales the image and sets it to label - * - * @param imageItem source image and url data - */ - private void setDiagram(@NotNull final ImageItem imageItem) { - originalImage = imageItem.getImage(); - Image scaledImage; - -// imageEditorUI = new ImageEditorUI(null); -// add(imageEditorUI); -// imageEditorUI.setImageProvider(new ImageDocument.CachedScaledImageProvider() { -// private Double zoom; -// private BufferedImage bufferedImage; +//package org.plantuml.idea.toolwindow; +// +//import com.intellij.openapi.Disposable; +//import com.intellij.openapi.actionSystem.*; +//import com.intellij.openapi.diagnostic.Logger; +//import com.intellij.ui.ColoredSideBorder; +//import com.intellij.ui.JBColor; +//import com.intellij.ui.PopupHandler; +//import com.intellij.ui.components.JBLayeredPane; +//import com.intellij.ui.components.Magnificator; +//import org.intellij.images.editor.ImageDocument; +//import org.intellij.images.ui.ImageComponent; +//import org.jetbrains.annotations.NotNull; +//import org.jetbrains.annotations.Nullable; +//import org.plantuml.idea.action.context.*; +//import org.plantuml.idea.lang.settings.PlantUmlSettings; +//import org.plantuml.idea.rendering.ImageItem; +//import org.plantuml.idea.rendering.RenderRequest; +// +//import javax.swing.*; +//import javax.swing.event.ChangeEvent; +//import javax.swing.event.ChangeListener; +//import java.awt.*; +//import java.awt.event.MouseAdapter; +//import java.awt.event.MouseEvent; +//import java.awt.image.BufferedImage; +//import java.io.IOException; +//import java.net.URI; +// +///** +// * TODO CopyDiagramToClipboardContextAction not working +// */ +//public class PlantUmlImagePanelSvg extends JPanel implements Disposable { +// private static final AnAction[] AN_ACTIONS = { +// new SaveDiagramToFileContextAction(), +// new CopyDiagramToClipboardContextAction(), +// Separator.getInstance(), +// new CopyDiagramAsTxtToClipboardContextAction(), +// new CopyDiagramAsUnicodeTxtToClipboardContextAction(), +// Separator.getInstance(), +// new CopyDiagramAsLatexToClipboardContextAction(), +// new CopyDiagramAsTikzCodeToClipboardContextAction(), +// Separator.getInstance(), +// new ExternalOpenDiagramAsPNGAction(), +// new ExternalOpenDiagramAsSVGAction(), +// Separator.getInstance(), +// new CopyPlantUmlServerLinkContextAction() +// }; +// private static final ActionPopupMenu ACTION_POPUP_MENU = ActionManager.getInstance().createActionPopupMenu(ActionPlaces.UNKNOWN, new ActionGroup() { +// +// @NotNull +// @Override +// public AnAction[] getChildren(@Nullable AnActionEvent e) { +// return AN_ACTIONS; +// } +// }); +// +// private static Logger logger = Logger.getInstance(PlantUmlImagePanelSvg.class); +// private RenderRequest renderRequest; +// private ImageItem imageWithData; +// private Image originalImage; +// private ImageComponent imageComponent; +// +// public PlantUmlImagePanelSvg(ImageItem imageWithData, int i, RenderRequest renderRequest) { +// this.imageWithData = imageWithData; +// setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); +// +// setup(this.imageWithData, i, renderRequest); +// } +// +// public ImageItem getImageWithData() { +// return imageWithData; +// } +// +// public int getPage() { +// return imageWithData.getPage(); +// } +// +// public RenderRequest getRenderRequest() { +// return renderRequest; +// } +// +// public void setup(@NotNull ImageItem imageWithData, int i, RenderRequest renderRequest) { +// setOpaque(true); +// setBackground(JBColor.WHITE); +// if (imageWithData.hasImage()) { +// setDiagram(imageWithData); +// } else { +// add(new JLabel("page not rendered, probably plugin error, please report it and try to hit reload")); +// } +// this.renderRequest = renderRequest; +// } +// +// /** +// * Scales the image and sets it to label +// * +// * @param imageItem source image and url data +// */ +// private void setDiagram(@NotNull final ImageItem imageItem) { +// originalImage = imageItem.getImage(); +// Image scaledImage; +// +//// imageEditorUI = new ImageEditorUI(null); +//// add(imageEditorUI); +//// imageEditorUI.setImageProvider(new ImageDocument.CachedScaledImageProvider() { +//// private Double zoom; +//// private BufferedImage bufferedImage; +//// +//// @Override +//// public BufferedImage apply(Double aDouble, Component component) { +//// try { +//// if (!aDouble.equals(zoom)) { +//// zoom = aDouble; +//// bufferedImage = loadWithoutCache(null, new ByteArrayInputStream(imageItem.getImageBytes()), zoom, null); +//// return bufferedImage; +//// } +//// } catch (IOException e) { +//// throw new RuntimeException(e); +//// } +//// return bufferedImage; +//// } +//// }, null); +// +// +// imageComponent = new ImageComponent(); +// imageComponent.setBackground(Color.WHITE); +// imageComponent.setOpaque(true); +// imageComponent.addMouseListener(new PopupHandler() { // // @Override -// public BufferedImage apply(Double aDouble, Component component) { -// try { -// if (!aDouble.equals(zoom)) { -// zoom = aDouble; -// bufferedImage = loadWithoutCache(null, new ByteArrayInputStream(imageItem.getImageBytes()), zoom, null); -// return bufferedImage; +// public void invokePopup(Component comp, int x, int y) { +// ACTION_POPUP_MENU.getComponent().show(comp, x, y); +// } +// }); +// +// ImageContainerPane comp = new ImageContainerPane(imageComponent); +// comp.setAlignmentX(Component.LEFT_ALIGNMENT); +// comp.setAlignmentY(Component.TOP_ALIGNMENT); +// add(comp); +// final ChangeListener changeListener = new DocumentChangeListener(); +// imageComponent.getDocument().addChangeListener(changeListener); +// imageComponent.getDocument().setValue(imageItem.getImage()); +// +// boolean showUrlLinksBorder = PlantUmlSettings.getInstance().isShowUrlLinksBorder(); +// +// for (ImageItem.UrlData url : imageItem.getUrls()) { +// final URI uri = url.getUri(); +// JLabel button = new JLabel(); +// if (showUrlLinksBorder) { +// button.setBorder(new ColoredSideBorder(Color.RED, Color.RED, Color.RED, Color.RED, 1)); +// } +// +// int tolerance = 5; +// Rectangle area = url.getClickArea(); +// area = new Rectangle(area.x, area.y, area.width + tolerance, area.height + tolerance); +// Point location = area.getLocation(); +// Dimension size = area.getSize(); +// +// button.setLocation(location); +// button.setSize(size); +// +// button.setCursor(new Cursor(Cursor.HAND_CURSOR)); +// +// //When user clicks on item, url is opened in default system browser +// button.addMouseListener(new MouseAdapter() { +// @Override +// public void mouseClicked(MouseEvent e) { +// try { +// Desktop.getDesktop().browse(uri); +// } catch (IOException ex) { +// logger.warn(ex); // } -// } catch (IOException e) { -// throw new RuntimeException(e); // } -// return bufferedImage; -// } -// }, null); - - - imageComponent = new ImageComponent(); - imageComponent.setBackground(Color.WHITE); - imageComponent.setOpaque(true); - imageComponent.addMouseListener(new PopupHandler() { - - @Override - public void invokePopup(Component comp, int x, int y) { - ACTION_POPUP_MENU.getComponent().show(comp, x, y); - } - }); - - ImageContainerPane comp = new ImageContainerPane(imageComponent); - comp.setAlignmentX(Component.LEFT_ALIGNMENT); - comp.setAlignmentY(Component.TOP_ALIGNMENT); - add(comp); - final ChangeListener changeListener = new DocumentChangeListener(); - imageComponent.getDocument().addChangeListener(changeListener); - imageComponent.getDocument().setValue(imageItem.getImage()); - - boolean showUrlLinksBorder = PlantUmlSettings.getInstance().isShowUrlLinksBorder(); - - for (ImageItem.UrlData url : imageItem.getUrls()) { - final URI uri = url.getUri(); - JLabel button = new JLabel(); - if (showUrlLinksBorder) { - button.setBorder(new ColoredSideBorder(Color.RED, Color.RED, Color.RED, Color.RED, 1)); - } - - int tolerance = 5; - Rectangle area = url.getClickArea(); - area = new Rectangle(area.x, area.y, area.width + tolerance, area.height + tolerance); - Point location = area.getLocation(); - Dimension size = area.getSize(); - - button.setLocation(location); - button.setSize(size); - - button.setCursor(new Cursor(Cursor.HAND_CURSOR)); - - //When user clicks on item, url is opened in default system browser - button.addMouseListener(new MouseAdapter() { - @Override - public void mouseClicked(MouseEvent e) { - try { - Desktop.getDesktop().browse(uri); - } catch (IOException ex) { - logger.warn(ex); - } - } - }); - - - imageComponent.add(button); - } - } - - public Image getOriginalImage() { - return originalImage; - } - - - @Override - public void dispose() { - } - - private class DocumentChangeListener implements ChangeListener { - @Override - public void stateChanged(@NotNull ChangeEvent e) { - updateImageComponentSize(); - - ImageDocument document = imageComponent.getDocument(); - BufferedImage value = document.getValue(); - - // CardLayout layout = (CardLayout)contentPanel.getLayout(); - // layout.show(contentPanel, value != null ? IMAGE_PANEL : ERROR_PANEL); - - // updateInfo(); - - revalidate(); - repaint(); - } - } - - private void updateImageComponentSize() { - Rectangle bounds = imageComponent.getDocument().getBounds(); - if (bounds != null) { - final double zoom = 1d; - imageComponent.setCanvasSize((int) Math.ceil(bounds.width * zoom), (int) Math.ceil(bounds.height * zoom)); - } - } - - private final class ImageContainerPane extends JBLayeredPane { - private final ImageComponent imageComponent; - - ImageContainerPane(final ImageComponent imageComponent) { - this.imageComponent = imageComponent; - add(imageComponent); - - putClientProperty(Magnificator.CLIENT_PROPERTY_KEY, new Magnificator() { - @Override - public Point magnify(double scale, Point at) { - Point locationBefore = imageComponent.getLocation(); - // ImageZoomModel model = editor != null ? editor.getZoomModel() : getZoomModel(); - // double factor = model.getZoomFactor(); - // model.setZoomFactor(scale * factor); - return new Point(((int) ((at.x - Math.max(scale > 1.0 ? locationBefore.x : 0, 0)) * scale)), - ((int) ((at.y - Math.max(scale > 1.0 ? locationBefore.y : 0, 0)) * scale))); - } - }); - } - - private void centerComponents() { - Rectangle bounds = getBounds(); - Point point = imageComponent.getLocation(); - // in embedded mode images should be left-side aligned - boolean b = false; - point.x = b ? 0 : (bounds.width - imageComponent.getWidth()) / 2; - point.y = (bounds.height - imageComponent.getHeight()) / 2; - imageComponent.setLocation(point); - } - - @Override - public void invalidate() { -// centerComponents(); - super.invalidate(); - } - - @Override - public Dimension getPreferredSize() { - return imageComponent.getSize(); - } - - @Override - public Dimension getMaximumSize() { - return imageComponent.getSize(); -// return super.getMaximumSize(); - } - } -} +// }); +// +// +// imageComponent.add(button); +// } +// } +// +// public Image getOriginalImage() { +// return originalImage; +// } +// +// +// @Override +// public void dispose() { +// } +// +// private class DocumentChangeListener implements ChangeListener { +// @Override +// public void stateChanged(@NotNull ChangeEvent e) { +// updateImageComponentSize(); +// +// ImageDocument document = imageComponent.getDocument(); +// BufferedImage value = document.getValue(); +// +// // CardLayout layout = (CardLayout)contentPanel.getLayout(); +// // layout.show(contentPanel, value != null ? IMAGE_PANEL : ERROR_PANEL); +// +// // updateInfo(); +// +// revalidate(); +// repaint(); +// } +// } +// +// private void updateImageComponentSize() { +// Rectangle bounds = imageComponent.getDocument().getBounds(); +// if (bounds != null) { +// final double zoom = 1d; +// imageComponent.setCanvasSize((int) Math.ceil(bounds.width * zoom), (int) Math.ceil(bounds.height * zoom)); +// } +// } +// +// private final class ImageContainerPane extends JBLayeredPane { +// private final ImageComponent imageComponent; +// +// ImageContainerPane(final ImageComponent imageComponent) { +// this.imageComponent = imageComponent; +// add(imageComponent); +// +// putClientProperty(Magnificator.CLIENT_PROPERTY_KEY, new Magnificator() { +// @Override +// public Point magnify(double scale, Point at) { +// Point locationBefore = imageComponent.getLocation(); +// // ImageZoomModel model = editor != null ? editor.getZoomModel() : getZoomModel(); +// // double factor = model.getZoomFactor(); +// // model.setZoomFactor(scale * factor); +// return new Point(((int) ((at.x - Math.max(scale > 1.0 ? locationBefore.x : 0, 0)) * scale)), +// ((int) ((at.y - Math.max(scale > 1.0 ? locationBefore.y : 0, 0)) * scale))); +// } +// }); +// } +// +// private void centerComponents() { +// Rectangle bounds = getBounds(); +// Point point = imageComponent.getLocation(); +// // in embedded mode images should be left-side aligned +// boolean b = false; +// point.x = b ? 0 : (bounds.width - imageComponent.getWidth()) / 2; +// point.y = (bounds.height - imageComponent.getHeight()) / 2; +// imageComponent.setLocation(point); +// } +// +// @Override +// public void invalidate() { +//// centerComponents(); +// super.invalidate(); +// } +// +// @Override +// public Dimension getPreferredSize() { +// return imageComponent.getSize(); +// } +// +// @Override +// public Dimension getMaximumSize() { +// return imageComponent.getSize(); +//// return super.getMaximumSize(); +// } +// } +//} diff --git a/src/org/plantuml/idea/toolwindow/PlantUmlToolWindow.java b/src/org/plantuml/idea/toolwindow/PlantUmlToolWindow.java index 0fbc97ae..907ad3e3 100644 --- a/src/org/plantuml/idea/toolwindow/PlantUmlToolWindow.java +++ b/src/org/plantuml/idea/toolwindow/PlantUmlToolWindow.java @@ -425,9 +425,9 @@ public void displayImage(RenderCacheItem cacheItem, int pageNumber, ImageItem im if (imageWithData == null) { throw new RuntimeException("trying to display null image. selectedPage=" + selectedPage + ", nullPage=" + pageNumber + ", cacheItem=" + cacheItem); } - JComponent component; + JComponent component = null; if (cacheItem.getRenderRequest().getFormat() == PlantUml.ImageFormat.SVG) { - component = new PlantUmlImagePanelSvg(imageWithData, pageNumber, cacheItem.getRenderRequest()); +// component = new PlantUmlImagePanelSvg(imageWithData, pageNumber, cacheItem.getRenderRequest()); } else { component = new PlantUmlImageLabel(imagesPanel, imageWithData, pageNumber, cacheItem.getRenderRequest()); } diff --git a/src/org/plantuml/idea/toolwindow/Usage.java b/src/org/plantuml/idea/toolwindow/Usage.java index 1bf443b0..3778a688 100644 --- a/src/org/plantuml/idea/toolwindow/Usage.java +++ b/src/org/plantuml/idea/toolwindow/Usage.java @@ -17,7 +17,7 @@ public class Usage extends JTextArea { + " - disable syntax check\n" + " - enable partial rendering - renders each page on it's own, useful for big sequence diagram files\n" + "\nPerformance tips:\n" - + "- disable automatic rendering and use Update (Alt+D) or Reload (Alt+F) buttons\n" + + "- disable automatic rendering and use Update (Ctrl Alt Shift F) or Reload (Ctrl Alt Shift G) buttons\n" + "- do not put @newpage into included files (it prohibits incremental rendering)\n" + "- try to enable partial rendering\n" + "- disable syntax checking\n" diff --git a/src/org/plantuml/idea/util/Utils.java b/src/org/plantuml/idea/util/Utils.java index 12bf24ff..184bbad6 100644 --- a/src/org/plantuml/idea/util/Utils.java +++ b/src/org/plantuml/idea/util/Utils.java @@ -2,10 +2,17 @@ import com.intellij.openapi.fileTypes.FileType; 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.plantuml.idea.lang.PlantIUmlFileType; import org.plantuml.idea.lang.PlantUmlFileType; +import java.util.ArrayList; +import java.util.List; + +import static java.lang.Character.isLetter; + public class Utils { public static int asInt(String renderDelay, int defaultValue) { @@ -20,7 +27,63 @@ public static int asInt(String renderDelay, int defaultValue) { public static boolean isPlantUmlFileType(@NotNull PsiFile file) { FileType fileType = file.getFileType(); - return fileType.equals(PlantUmlFileType.PLANTUML_FILE_TYPE) || fileType.equals(PlantIUmlFileType.PLANTUML_FILE_TYPE); + return fileType.equals(PlantUmlFileType.INSTANCE) || fileType.equals(PlantIUmlFileType.PLANTUML_FILE_TYPE); } + public static boolean containsLetters(CharSequence s) { + for (int i = 0; i < s.length(); i++) { + if (isLetter(s.charAt(i))) { + return true; + } + } + return false; + } + + public static List rangesInside(String str, String open, String close) { + if (str != null && !StringUtils.isEmpty(open) && !StringUtils.isEmpty(close)) { + int strLen = str.length(); + if (strLen == 0) { + return null; + } else { + int closeLen = close.length(); + int openLen = open.length(); + List ranges = null; + + int end; + for (int pos = 0; pos < strLen - closeLen; pos = end + closeLen) { + int start = str.indexOf(open, pos); + if (start < 0) { + break; + } + + start += openLen; + end = str.indexOf(close, start); + if (end < 0) { + break; + } + if (ranges == null) { + ranges = new ArrayList<>(); + } + ranges.add(new IntRange(start, end)); + } + + return ranges; + } + } else { + return null; + } + } + + public static List join(List r1, List r2) { + if (r1 == null && r2 == null) { + return null; + } else if (r1 == null) { + return r2; + } else if (r2 == null) { + return r1; + } else { + r1.addAll(r2); + return r1; + } + } } diff --git a/test/org/plantuml/idea/lang/SimpleCodeInsightTest.java b/test/org/plantuml/idea/lang/SimpleCodeInsightTest.java new file mode 100644 index 00000000..2623de09 --- /dev/null +++ b/test/org/plantuml/idea/lang/SimpleCodeInsightTest.java @@ -0,0 +1,73 @@ +package org.plantuml.idea.lang; + +import com.intellij.codeInsight.generation.actions.CommentByLineCommentAction; +import com.intellij.psi.PsiElement; +import com.intellij.testFramework.fixtures.LightJavaCodeInsightFixtureTestCase; +import com.intellij.usageView.UsageInfo; +import org.plantuml.idea.grammar.psi.PumlItem; + +import java.util.Collection; + +public class SimpleCodeInsightTest extends LightJavaCodeInsightFixtureTestCase { + + /** + * @return path to test data file directory relative to working directory in the run configuration for this test. + */ + @Override + protected String getTestDataPath() { + return "testData"; + } + + + public void testAnnotator() { + myFixture.configureByFiles("DefaultTestData.puml"); + myFixture.checkHighlighting(false, false, true, true); + } + +// public void testFormatter() { +// myFixture.configureByFile("FormatterTestData.simple"); +// CodeStyle.getLanguageSettings(myFixture.getFile()).SPACE_AROUND_ASSIGNMENT_OPERATORS = true; +// CodeStyle.getLanguageSettings(myFixture.getFile()).KEEP_BLANK_LINES_IN_CODE = 2; +// WriteCommandAction.writeCommandAction(getProject()).run(() -> +// CodeStyleManager.getInstance(getProject()).reformatText( +// myFixture.getFile(), +// ContainerUtil.newArrayList(myFixture.getFile().getTextRange()) +// ) +// ); +// myFixture.checkResultByFile("DefaultTestData.puml"); +// } + + public void testRename() { + myFixture.configureByFiles("DefaultTestData.puml"); + myFixture.renameElementAtCaret("Foo:"); + myFixture.checkResultByFile("DefaultTestData.puml", "DefaultTestDataAfter.puml", false); + } + +// public void testFolding() { +// myFixture.configureByFile("DefaultTestData.puml"); +// myFixture.testFolding(getTestDataPath() + "/FoldingTestData.java"); +// } + + public void testFindUsages() { + Collection usageInfos = myFixture.testFindUsages("DefaultTestData.puml"); + assertEquals(3, usageInfos.size()); + } + + public void testCommenter() { + myFixture.configureByText(PlantUmlFileType.INSTANCE, "foo"); + CommentByLineCommentAction commentAction = new CommentByLineCommentAction(); + commentAction.actionPerformedImpl(getProject(), myFixture.getEditor()); + myFixture.checkResult("'foo"); + commentAction.actionPerformedImpl(getProject(), myFixture.getEditor()); + myFixture.checkResult("foo"); + } + + public void testReference() { + myFixture.configureByFiles("DefaultTestData.puml"); + PsiElement element = myFixture.getFile().findElementAt(myFixture.getCaretOffset()).getParent(); + String text = element.getText(); + + assertEquals("Bob", ((PumlItem) element.getReferences()[0].resolve()).getText()); + } + +} diff --git a/test/org/plantuml/idea/lang/SimpleParsingTest.java b/test/org/plantuml/idea/lang/SimpleParsingTest.java new file mode 100644 index 00000000..c8eb5799 --- /dev/null +++ b/test/org/plantuml/idea/lang/SimpleParsingTest.java @@ -0,0 +1,35 @@ +// Copyright 2000-2020 JetBrains s.r.o. and other contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. + +package org.plantuml.idea.lang; + +import com.intellij.testFramework.ParsingTestCase; + +public class SimpleParsingTest extends ParsingTestCase { + + public SimpleParsingTest() { + super("", "puml", new PlantUmlParserDefinition()); + } + + public void testParsingTestData() { + doTest(true); + } + + /** + * @return path to test data file directory relative to root of this module. + */ + @Override + protected String getTestDataPath() { + return "testData"; + } + + @Override + protected boolean skipSpaces() { + return false; + } + + @Override + protected boolean includeRanges() { + return true; + } + +} diff --git a/test/org/plantuml/idea/lang/annotator/PlantUmlExternalAnnotatorTest.java b/test/org/plantuml/idea/lang/annotator/PlantUmlExternalAnnotatorTest.java index 95cfce43..fbef3721 100644 --- a/test/org/plantuml/idea/lang/annotator/PlantUmlExternalAnnotatorTest.java +++ b/test/org/plantuml/idea/lang/annotator/PlantUmlExternalAnnotatorTest.java @@ -16,7 +16,7 @@ public void testLineCommentHighlight() { PsiFile psiFile = createForPUMLFile( "@startuml\n'Line Comment\nactor User/'Block Comment'/\n@enduml"); - FileAnnotationResult fileAnnotationResult = plantUmlExternalAnnotator.doAnnotate(psiFile); + FileAnnotationResult fileAnnotationResult = plantUmlExternalAnnotator.doAnnotate(new PlantUmlExternalAnnotator.Info(psiFile)); assertNotNull(fileAnnotationResult); @@ -33,7 +33,7 @@ public void testLineCommentHighlight_withWhitespacesBefore() { PsiFile psiFile = createForPUMLFile( "@startuml\n\t 'Line Comment\nactor User/'Block Comment'/\n@enduml"); - FileAnnotationResult fileAnnotationResult = plantUmlExternalAnnotator.doAnnotate(psiFile); + FileAnnotationResult fileAnnotationResult = plantUmlExternalAnnotator.doAnnotate(new PlantUmlExternalAnnotator.Info(psiFile)); assertNotNull(fileAnnotationResult); @@ -51,7 +51,7 @@ public void testBlockCommentHighlight() { PsiFile psiFile = createForPUMLFile( "@startuml\n/'Block Comment'/\nactor User/'Second Block Comment'/\n@enduml"); - FileAnnotationResult fileAnnotationResult = plantUmlExternalAnnotator.doAnnotate(psiFile); + FileAnnotationResult fileAnnotationResult = plantUmlExternalAnnotator.doAnnotate(new PlantUmlExternalAnnotator.Info(psiFile)); assertNotNull(fileAnnotationResult); diff --git a/testData/DefaultTestData.puml b/testData/DefaultTestData.puml new file mode 100644 index 00000000..c162c1c7 --- /dev/null +++ b/testData/DefaultTestData.puml @@ -0,0 +1,7 @@ +@startuml +Alice -> Bob: Authentication Request +Bob --> Alice: Authentication Response + +Alice -> Bob: Another authentication Request +Alice <-- Bob: another authentication Response +@enduml \ No newline at end of file diff --git a/testData/DefaultTestDataAfter.puml b/testData/DefaultTestDataAfter.puml new file mode 100644 index 00000000..bac24150 --- /dev/null +++ b/testData/DefaultTestDataAfter.puml @@ -0,0 +1,7 @@ +@startuml +Alice -> Foo: Authentication Request +Foo --> Alice: Authentication Response + +Alice -> Foo: Another authentication Request +Alice <-- Foo: another authentication Response +@enduml \ No newline at end of file diff --git a/testData/ParsingTestData.puml b/testData/ParsingTestData.puml new file mode 100644 index 00000000..2ff65f5f --- /dev/null +++ b/testData/ParsingTestData.puml @@ -0,0 +1,37 @@ +@startuml +Alice -> Bob: Authentication Request +Bob --> Alice: Authentication Response + +Alice -> Bob: Another authentication Request +Alice <-- Bob: another authentication Response +@enduml + + +@startuml +?-> Alice : ""?->""\n**short** to actor1 +[-> Alice : ""[->""\n**from start** to actor1 +[-> Bob : ""[->""\n**from start** to actor2 +?-> Bob : ""?->""\n**short** to actor2 +Alice ->] : ""->]""\nfrom actor1 **to end** +Alice ->? : ""->?""\n**short** from actor1 +Alice -> Bob : ""->"" \nfrom actor1 to actor2 +@enduml + + +@startuml +!pragma teoz true + +{start} Alice -> Bob : start doing things during duration +Bob -> Max : something +Max -> Bob : something else +{end} Bob -> Alice : finish + +{start} <-> {end} : some time + +@enduml + + +@startuml +ProductOfferingPrice -- ProductUsageSpec: ForUsage > +(ProductOfferingPrice, ProductUsageSpec) .. ProductUsageSpecCharacteristic +@enduml \ No newline at end of file diff --git a/testData/ParsingTestData.txt b/testData/ParsingTestData.txt new file mode 100644 index 00000000..90b3c65f --- /dev/null +++ b/testData/ParsingTestData.txt @@ -0,0 +1,500 @@ +FILE(0,910) + PumlItemImpl(@startuml)(0,9)(0,9) + PsiElement(PumlTokenType.IDENTIFIER)('@startuml')(0,9) + PsiElement(PumlTokenType.NEW_LINE_INDENT)('\n')(9,10) + PumlItemImpl(Alice)(10,15)(10,15) + PsiElement(PumlTokenType.IDENTIFIER)('Alice')(10,15) + PsiElement(PumlTokenType.WHITE_SPACE)(' ')(15,16) + PumlItemImpl(->)(16,18)(16,18) + PsiElement(PumlTokenType.OTHER)('->')(16,18) + PsiElement(PumlTokenType.WHITE_SPACE)(' ')(18,19) + PumlItemImpl(Bob)(19,22)(19,22) + PsiElement(PumlTokenType.IDENTIFIER)('Bob')(19,22) + PumlItemImpl(:)(22,23)(22,23) + PsiElement(PumlTokenType.OTHER)(':')(22,23) + PsiElement(PumlTokenType.WHITE_SPACE)(' ')(23,24) + PumlItemImpl(Authentication)(24,38)(24,38) + PsiElement(PumlTokenType.IDENTIFIER)('Authentication')(24,38) + PsiElement(PumlTokenType.WHITE_SPACE)(' ')(38,39) + PumlItemImpl(Request)(39,46)(39,46) + PsiElement(PumlTokenType.IDENTIFIER)('Request')(39,46) + PsiElement(PumlTokenType.NEW_LINE_INDENT)('\n')(46,47) + PumlItemImpl(Bob)(47,50)(47,50) + PsiElement(PumlTokenType.IDENTIFIER)('Bob')(47,50) + PsiElement(PumlTokenType.WHITE_SPACE)(' ')(50,51) + PumlItemImpl(-->)(51,54)(51,54) + PsiElement(PumlTokenType.OTHER)('-->')(51,54) + PsiElement(PumlTokenType.WHITE_SPACE)(' ')(54,55) + PumlItemImpl(Alice)(55,60)(55,60) + PsiElement(PumlTokenType.IDENTIFIER)('Alice')(55,60) + PumlItemImpl(:)(60,61)(60,61) + PsiElement(PumlTokenType.OTHER)(':')(60,61) + PsiElement(PumlTokenType.WHITE_SPACE)(' ')(61,62) + PumlItemImpl(Authentication)(62,76)(62,76) + PsiElement(PumlTokenType.IDENTIFIER)('Authentication')(62,76) + PsiElement(PumlTokenType.WHITE_SPACE)(' ')(76,77) + PumlItemImpl(Response)(77,85)(77,85) + PsiElement(PumlTokenType.IDENTIFIER)('Response')(77,85) + PsiElement(PumlTokenType.NEW_LINE_INDENT)('\n\n')(85,87) + PumlItemImpl(Alice)(87,92)(87,92) + PsiElement(PumlTokenType.IDENTIFIER)('Alice')(87,92) + PsiElement(PumlTokenType.WHITE_SPACE)(' ')(92,93) + PumlItemImpl(->)(93,95)(93,95) + PsiElement(PumlTokenType.OTHER)('->')(93,95) + PsiElement(PumlTokenType.WHITE_SPACE)(' ')(95,96) + PumlItemImpl(Bob)(96,99)(96,99) + PsiElement(PumlTokenType.IDENTIFIER)('Bob')(96,99) + PumlItemImpl(:)(99,100)(99,100) + PsiElement(PumlTokenType.OTHER)(':')(99,100) + PsiElement(PumlTokenType.WHITE_SPACE)(' ')(100,101) + PumlItemImpl(Another)(101,108)(101,108) + PsiElement(PumlTokenType.IDENTIFIER)('Another')(101,108) + PsiElement(PumlTokenType.WHITE_SPACE)(' ')(108,109) + PumlItemImpl(authentication)(109,123)(109,123) + PsiElement(PumlTokenType.IDENTIFIER)('authentication')(109,123) + PsiElement(PumlTokenType.WHITE_SPACE)(' ')(123,124) + PumlItemImpl(Request)(124,131)(124,131) + PsiElement(PumlTokenType.IDENTIFIER)('Request')(124,131) + PsiElement(PumlTokenType.NEW_LINE_INDENT)('\n')(131,132) + PumlItemImpl(Alice)(132,137)(132,137) + PsiElement(PumlTokenType.IDENTIFIER)('Alice')(132,137) + PsiElement(PumlTokenType.WHITE_SPACE)(' ')(137,138) + PumlItemImpl(<--)(138,141)(138,141) + PsiElement(PumlTokenType.OTHER)('<--')(138,141) + PsiElement(PumlTokenType.WHITE_SPACE)(' ')(141,142) + PumlItemImpl(Bob)(142,145)(142,145) + PsiElement(PumlTokenType.IDENTIFIER)('Bob')(142,145) + PumlItemImpl(:)(145,146)(145,146) + PsiElement(PumlTokenType.OTHER)(':')(145,146) + PsiElement(PumlTokenType.WHITE_SPACE)(' ')(146,147) + PumlItemImpl(another)(147,154)(147,154) + PsiElement(PumlTokenType.IDENTIFIER)('another')(147,154) + PsiElement(PumlTokenType.WHITE_SPACE)(' ')(154,155) + PumlItemImpl(authentication)(155,169)(155,169) + PsiElement(PumlTokenType.IDENTIFIER)('authentication')(155,169) + PsiElement(PumlTokenType.WHITE_SPACE)(' ')(169,170) + PumlItemImpl(Response)(170,178)(170,178) + PsiElement(PumlTokenType.IDENTIFIER)('Response')(170,178) + PsiElement(PumlTokenType.NEW_LINE_INDENT)('\n')(178,179) + PumlItemImpl(@enduml)(179,186)(179,186) + PsiElement(PumlTokenType.IDENTIFIER)('@enduml')(179,186) + PsiElement(PumlTokenType.NEW_LINE_INDENT)('\n\n\n')(186,189) + PumlItemImpl(@startuml)(189,198)(189,198) + PsiElement(PumlTokenType.IDENTIFIER)('@startuml')(189,198) + PsiElement(PumlTokenType.NEW_LINE_INDENT)('\n')(198,199) + PumlItemImpl(?->)(199,202)(199,202) + PsiElement(PumlTokenType.OTHER)('?->')(199,202) + PsiElement(PumlTokenType.WHITE_SPACE)(' ')(202,203) + PumlItemImpl(Alice)(203,208)(203,208) + PsiElement(PumlTokenType.IDENTIFIER)('Alice')(203,208) + PsiElement(PumlTokenType.WHITE_SPACE)(' ')(208,212) + PumlItemImpl(:)(212,213)(212,213) + PsiElement(PumlTokenType.OTHER)(':')(212,213) + PsiElement(PumlTokenType.WHITE_SPACE)(' ')(213,214) + PumlItemImpl(")(214,215)(214,215) + PsiElement(PumlTokenType.OTHER)('"')(214,215) + PumlItemImpl("?->")(215,220)(215,220) + PsiElement(PumlTokenType.IDENTIFIER)('"?->"')(215,220) + PumlItemImpl(")(220,221)(220,221) + PsiElement(PumlTokenType.OTHER)('"')(220,221) + PumlItemImpl(\)(221,222)(221,222) + PsiElement(PumlTokenType.OTHER)('\')(221,222) + PumlItemImpl(n)(222,223)(222,223) + PsiElement(PumlTokenType.IDENTIFIER)('n')(222,223) + PumlItemImpl(**)(223,225)(223,225) + PsiElement(PumlTokenType.OTHER)('**')(223,225) + PumlItemImpl(short)(225,230)(225,230) + PsiElement(PumlTokenType.IDENTIFIER)('short')(225,230) + PumlItemImpl(**)(230,232)(230,232) + PsiElement(PumlTokenType.OTHER)('**')(230,232) + PsiElement(PumlTokenType.WHITE_SPACE)(' ')(232,233) + PumlItemImpl(to)(233,235)(233,235) + PsiElement(PumlTokenType.IDENTIFIER)('to')(233,235) + PsiElement(PumlTokenType.WHITE_SPACE)(' ')(235,236) + PumlItemImpl(actor1)(236,242)(236,242) + PsiElement(PumlTokenType.IDENTIFIER)('actor1')(236,242) + PsiElement(PumlTokenType.NEW_LINE_INDENT)('\n')(242,243) + PumlItemImpl([)(243,244)(243,244) + PsiElement(PumlTokenType.OTHER)('[')(243,244) + PumlItemImpl(->)(244,246)(244,246) + PsiElement(PumlTokenType.OTHER)('->')(244,246) + PsiElement(PumlTokenType.WHITE_SPACE)(' ')(246,247) + PumlItemImpl(Alice)(247,252)(247,252) + PsiElement(PumlTokenType.IDENTIFIER)('Alice')(247,252) + PsiElement(PumlTokenType.WHITE_SPACE)(' ')(252,256) + PumlItemImpl(:)(256,257)(256,257) + PsiElement(PumlTokenType.OTHER)(':')(256,257) + PsiElement(PumlTokenType.WHITE_SPACE)(' ')(257,258) + PumlItemImpl(")(258,259)(258,259) + PsiElement(PumlTokenType.OTHER)('"')(258,259) + PumlItemImpl("[->")(259,264)(259,264) + PsiElement(PumlTokenType.IDENTIFIER)('"[->"')(259,264) + PumlItemImpl(")(264,265)(264,265) + PsiElement(PumlTokenType.OTHER)('"')(264,265) + PumlItemImpl(\)(265,266)(265,266) + PsiElement(PumlTokenType.OTHER)('\')(265,266) + PumlItemImpl(n)(266,267)(266,267) + PsiElement(PumlTokenType.IDENTIFIER)('n')(266,267) + PumlItemImpl(**)(267,269)(267,269) + PsiElement(PumlTokenType.OTHER)('**')(267,269) + PumlItemImpl(from)(269,273)(269,273) + PsiElement(PumlTokenType.IDENTIFIER)('from')(269,273) + PsiElement(PumlTokenType.WHITE_SPACE)(' ')(273,274) + PumlItemImpl(start)(274,279)(274,279) + PsiElement(PumlTokenType.IDENTIFIER)('start')(274,279) + PumlItemImpl(**)(279,281)(279,281) + PsiElement(PumlTokenType.OTHER)('**')(279,281) + PsiElement(PumlTokenType.WHITE_SPACE)(' ')(281,282) + PumlItemImpl(to)(282,284)(282,284) + PsiElement(PumlTokenType.IDENTIFIER)('to')(282,284) + PsiElement(PumlTokenType.WHITE_SPACE)(' ')(284,285) + PumlItemImpl(actor1)(285,291)(285,291) + PsiElement(PumlTokenType.IDENTIFIER)('actor1')(285,291) + PsiElement(PumlTokenType.NEW_LINE_INDENT)('\n')(291,292) + PumlItemImpl([)(292,293)(292,293) + PsiElement(PumlTokenType.OTHER)('[')(292,293) + PumlItemImpl(->)(293,295)(293,295) + PsiElement(PumlTokenType.OTHER)('->')(293,295) + PsiElement(PumlTokenType.WHITE_SPACE)(' ')(295,296) + PumlItemImpl(Bob)(296,299)(296,299) + PsiElement(PumlTokenType.IDENTIFIER)('Bob')(296,299) + PsiElement(PumlTokenType.WHITE_SPACE)(' ')(299,305) + PumlItemImpl(:)(305,306)(305,306) + PsiElement(PumlTokenType.OTHER)(':')(305,306) + PsiElement(PumlTokenType.WHITE_SPACE)(' ')(306,307) + PumlItemImpl(")(307,308)(307,308) + PsiElement(PumlTokenType.OTHER)('"')(307,308) + PumlItemImpl("[->")(308,313)(308,313) + PsiElement(PumlTokenType.IDENTIFIER)('"[->"')(308,313) + PumlItemImpl(")(313,314)(313,314) + PsiElement(PumlTokenType.OTHER)('"')(313,314) + PumlItemImpl(\)(314,315)(314,315) + PsiElement(PumlTokenType.OTHER)('\')(314,315) + PumlItemImpl(n)(315,316)(315,316) + PsiElement(PumlTokenType.IDENTIFIER)('n')(315,316) + PumlItemImpl(**)(316,318)(316,318) + PsiElement(PumlTokenType.OTHER)('**')(316,318) + PumlItemImpl(from)(318,322)(318,322) + PsiElement(PumlTokenType.IDENTIFIER)('from')(318,322) + PsiElement(PumlTokenType.WHITE_SPACE)(' ')(322,323) + PumlItemImpl(start)(323,328)(323,328) + PsiElement(PumlTokenType.IDENTIFIER)('start')(323,328) + PumlItemImpl(**)(328,330)(328,330) + PsiElement(PumlTokenType.OTHER)('**')(328,330) + PsiElement(PumlTokenType.WHITE_SPACE)(' ')(330,331) + PumlItemImpl(to)(331,333)(331,333) + PsiElement(PumlTokenType.IDENTIFIER)('to')(331,333) + PsiElement(PumlTokenType.WHITE_SPACE)(' ')(333,334) + PumlItemImpl(actor2)(334,340)(334,340) + PsiElement(PumlTokenType.IDENTIFIER)('actor2')(334,340) + PsiElement(PumlTokenType.NEW_LINE_INDENT)('\n')(340,341) + PumlItemImpl(?->)(341,344)(341,344) + PsiElement(PumlTokenType.OTHER)('?->')(341,344) + PsiElement(PumlTokenType.WHITE_SPACE)(' ')(344,345) + PumlItemImpl(Bob)(345,348)(345,348) + PsiElement(PumlTokenType.IDENTIFIER)('Bob')(345,348) + PsiElement(PumlTokenType.WHITE_SPACE)(' ')(348,354) + PumlItemImpl(:)(354,355)(354,355) + PsiElement(PumlTokenType.OTHER)(':')(354,355) + PsiElement(PumlTokenType.WHITE_SPACE)(' ')(355,356) + PumlItemImpl(")(356,357)(356,357) + PsiElement(PumlTokenType.OTHER)('"')(356,357) + PumlItemImpl("?->")(357,362)(357,362) + PsiElement(PumlTokenType.IDENTIFIER)('"?->"')(357,362) + PumlItemImpl(")(362,363)(362,363) + PsiElement(PumlTokenType.OTHER)('"')(362,363) + PumlItemImpl(\)(363,364)(363,364) + PsiElement(PumlTokenType.OTHER)('\')(363,364) + PumlItemImpl(n)(364,365)(364,365) + PsiElement(PumlTokenType.IDENTIFIER)('n')(364,365) + PumlItemImpl(**)(365,367)(365,367) + PsiElement(PumlTokenType.OTHER)('**')(365,367) + PumlItemImpl(short)(367,372)(367,372) + PsiElement(PumlTokenType.IDENTIFIER)('short')(367,372) + PumlItemImpl(**)(372,374)(372,374) + PsiElement(PumlTokenType.OTHER)('**')(372,374) + PsiElement(PumlTokenType.WHITE_SPACE)(' ')(374,375) + PumlItemImpl(to)(375,377)(375,377) + PsiElement(PumlTokenType.IDENTIFIER)('to')(375,377) + PsiElement(PumlTokenType.WHITE_SPACE)(' ')(377,378) + PumlItemImpl(actor2)(378,384)(378,384) + PsiElement(PumlTokenType.IDENTIFIER)('actor2')(378,384) + PsiElement(PumlTokenType.NEW_LINE_INDENT)('\n')(384,385) + PumlItemImpl(Alice)(385,390)(385,390) + PsiElement(PumlTokenType.IDENTIFIER)('Alice')(385,390) + PsiElement(PumlTokenType.WHITE_SPACE)(' ')(390,391) + PumlItemImpl(->])(391,394)(391,394) + PsiElement(PumlTokenType.OTHER)('->]')(391,394) + PsiElement(PumlTokenType.WHITE_SPACE)(' ')(394,398) + PumlItemImpl(:)(398,399)(398,399) + PsiElement(PumlTokenType.OTHER)(':')(398,399) + PsiElement(PumlTokenType.WHITE_SPACE)(' ')(399,400) + PumlItemImpl(")(400,401)(400,401) + PsiElement(PumlTokenType.OTHER)('"')(400,401) + PumlItemImpl("->]")(401,406)(401,406) + PsiElement(PumlTokenType.IDENTIFIER)('"->]"')(401,406) + PumlItemImpl(")(406,407)(406,407) + PsiElement(PumlTokenType.OTHER)('"')(406,407) + PumlItemImpl(\)(407,408)(407,408) + PsiElement(PumlTokenType.OTHER)('\')(407,408) + PumlItemImpl(nfrom)(408,413)(408,413) + PsiElement(PumlTokenType.IDENTIFIER)('nfrom')(408,413) + PsiElement(PumlTokenType.WHITE_SPACE)(' ')(413,414) + PumlItemImpl(actor1)(414,420)(414,420) + PsiElement(PumlTokenType.IDENTIFIER)('actor1')(414,420) + PsiElement(PumlTokenType.WHITE_SPACE)(' ')(420,421) + PumlItemImpl(**)(421,423)(421,423) + PsiElement(PumlTokenType.OTHER)('**')(421,423) + PumlItemImpl(to)(423,425)(423,425) + PsiElement(PumlTokenType.IDENTIFIER)('to')(423,425) + PsiElement(PumlTokenType.WHITE_SPACE)(' ')(425,426) + PumlItemImpl(end)(426,429)(426,429) + PsiElement(PumlTokenType.IDENTIFIER)('end')(426,429) + PumlItemImpl(**)(429,431)(429,431) + PsiElement(PumlTokenType.OTHER)('**')(429,431) + PsiElement(PumlTokenType.NEW_LINE_INDENT)('\n')(431,432) + PumlItemImpl(Alice)(432,437)(432,437) + PsiElement(PumlTokenType.IDENTIFIER)('Alice')(432,437) + PsiElement(PumlTokenType.WHITE_SPACE)(' ')(437,438) + PumlItemImpl(->?)(438,441)(438,441) + PsiElement(PumlTokenType.OTHER)('->?')(438,441) + PsiElement(PumlTokenType.WHITE_SPACE)(' ')(441,445) + PumlItemImpl(:)(445,446)(445,446) + PsiElement(PumlTokenType.OTHER)(':')(445,446) + PsiElement(PumlTokenType.WHITE_SPACE)(' ')(446,447) + PumlItemImpl(")(447,448)(447,448) + PsiElement(PumlTokenType.OTHER)('"')(447,448) + PumlItemImpl("->?")(448,453)(448,453) + PsiElement(PumlTokenType.IDENTIFIER)('"->?"')(448,453) + PumlItemImpl(")(453,454)(453,454) + PsiElement(PumlTokenType.OTHER)('"')(453,454) + PumlItemImpl(\)(454,455)(454,455) + PsiElement(PumlTokenType.OTHER)('\')(454,455) + PumlItemImpl(n)(455,456)(455,456) + PsiElement(PumlTokenType.IDENTIFIER)('n')(455,456) + PumlItemImpl(**)(456,458)(456,458) + PsiElement(PumlTokenType.OTHER)('**')(456,458) + PumlItemImpl(short)(458,463)(458,463) + PsiElement(PumlTokenType.IDENTIFIER)('short')(458,463) + PumlItemImpl(**)(463,465)(463,465) + PsiElement(PumlTokenType.OTHER)('**')(463,465) + PsiElement(PumlTokenType.WHITE_SPACE)(' ')(465,466) + PumlItemImpl(from)(466,470)(466,470) + PsiElement(PumlTokenType.IDENTIFIER)('from')(466,470) + PsiElement(PumlTokenType.WHITE_SPACE)(' ')(470,471) + PumlItemImpl(actor1)(471,477)(471,477) + PsiElement(PumlTokenType.IDENTIFIER)('actor1')(471,477) + PsiElement(PumlTokenType.NEW_LINE_INDENT)('\n')(477,478) + PumlItemImpl(Alice)(478,483)(478,483) + PsiElement(PumlTokenType.IDENTIFIER)('Alice')(478,483) + PsiElement(PumlTokenType.WHITE_SPACE)(' ')(483,484) + PumlItemImpl(->)(484,486)(484,486) + PsiElement(PumlTokenType.OTHER)('->')(484,486) + PsiElement(PumlTokenType.WHITE_SPACE)(' ')(486,487) + PumlItemImpl(Bob)(487,490)(487,490) + PsiElement(PumlTokenType.IDENTIFIER)('Bob')(487,490) + PsiElement(PumlTokenType.WHITE_SPACE)(' ')(490,491) + PumlItemImpl(:)(491,492)(491,492) + PsiElement(PumlTokenType.OTHER)(':')(491,492) + PsiElement(PumlTokenType.WHITE_SPACE)(' ')(492,493) + PumlItemImpl(")(493,494)(493,494) + PsiElement(PumlTokenType.OTHER)('"')(493,494) + PumlItemImpl("->")(494,498)(494,498) + PsiElement(PumlTokenType.IDENTIFIER)('"->"')(494,498) + PumlItemImpl(")(498,499)(498,499) + PsiElement(PumlTokenType.OTHER)('"')(498,499) + PsiElement(PumlTokenType.WHITE_SPACE)(' ')(499,500) + PumlItemImpl(\)(500,501)(500,501) + PsiElement(PumlTokenType.OTHER)('\')(500,501) + PumlItemImpl(nfrom)(501,506)(501,506) + PsiElement(PumlTokenType.IDENTIFIER)('nfrom')(501,506) + PsiElement(PumlTokenType.WHITE_SPACE)(' ')(506,507) + PumlItemImpl(actor1)(507,513)(507,513) + PsiElement(PumlTokenType.IDENTIFIER)('actor1')(507,513) + PsiElement(PumlTokenType.WHITE_SPACE)(' ')(513,514) + PumlItemImpl(to)(514,516)(514,516) + PsiElement(PumlTokenType.IDENTIFIER)('to')(514,516) + PsiElement(PumlTokenType.WHITE_SPACE)(' ')(516,517) + PumlItemImpl(actor2)(517,523)(517,523) + PsiElement(PumlTokenType.IDENTIFIER)('actor2')(517,523) + PsiElement(PumlTokenType.NEW_LINE_INDENT)('\n')(523,524) + PumlItemImpl(@enduml)(524,531)(524,531) + PsiElement(PumlTokenType.IDENTIFIER)('@enduml')(524,531) + PsiElement(PumlTokenType.NEW_LINE_INDENT)('\n\n\n')(531,534) + PumlItemImpl(@startuml)(534,543)(534,543) + PsiElement(PumlTokenType.IDENTIFIER)('@startuml')(534,543) + PsiElement(PumlTokenType.NEW_LINE_INDENT)('\n')(543,544) + PumlItemImpl(!)(544,545)(544,545) + PsiElement(PumlTokenType.OTHER)('!')(544,545) + PumlItemImpl(pragma)(545,551)(545,551) + PsiElement(PumlTokenType.IDENTIFIER)('pragma')(545,551) + PsiElement(PumlTokenType.WHITE_SPACE)(' ')(551,552) + PumlItemImpl(teoz)(552,556)(552,556) + PsiElement(PumlTokenType.IDENTIFIER)('teoz')(552,556) + PsiElement(PumlTokenType.WHITE_SPACE)(' ')(556,557) + PumlItemImpl(true)(557,561)(557,561) + PsiElement(PumlTokenType.IDENTIFIER)('true')(557,561) + PsiElement(PumlTokenType.NEW_LINE_INDENT)('\n\n')(561,563) + PumlItemImpl({)(563,564)(563,564) + PsiElement(PumlTokenType.OTHER)('{')(563,564) + PumlItemImpl(start)(564,569)(564,569) + PsiElement(PumlTokenType.IDENTIFIER)('start')(564,569) + PumlItemImpl(})(569,570)(569,570) + PsiElement(PumlTokenType.OTHER)('}')(569,570) + PsiElement(PumlTokenType.WHITE_SPACE)(' ')(570,571) + PumlItemImpl(Alice)(571,576)(571,576) + PsiElement(PumlTokenType.IDENTIFIER)('Alice')(571,576) + PsiElement(PumlTokenType.WHITE_SPACE)(' ')(576,577) + PumlItemImpl(->)(577,579)(577,579) + PsiElement(PumlTokenType.OTHER)('->')(577,579) + PsiElement(PumlTokenType.WHITE_SPACE)(' ')(579,580) + PumlItemImpl(Bob)(580,583)(580,583) + PsiElement(PumlTokenType.IDENTIFIER)('Bob')(580,583) + PsiElement(PumlTokenType.WHITE_SPACE)(' ')(583,584) + PumlItemImpl(:)(584,585)(584,585) + PsiElement(PumlTokenType.OTHER)(':')(584,585) + PsiElement(PumlTokenType.WHITE_SPACE)(' ')(585,586) + PumlItemImpl(start)(586,591)(586,591) + PsiElement(PumlTokenType.IDENTIFIER)('start')(586,591) + PsiElement(PumlTokenType.WHITE_SPACE)(' ')(591,592) + PumlItemImpl(doing)(592,597)(592,597) + PsiElement(PumlTokenType.IDENTIFIER)('doing')(592,597) + PsiElement(PumlTokenType.WHITE_SPACE)(' ')(597,598) + PumlItemImpl(things)(598,604)(598,604) + PsiElement(PumlTokenType.IDENTIFIER)('things')(598,604) + PsiElement(PumlTokenType.WHITE_SPACE)(' ')(604,605) + PumlItemImpl(during)(605,611)(605,611) + PsiElement(PumlTokenType.IDENTIFIER)('during')(605,611) + PsiElement(PumlTokenType.WHITE_SPACE)(' ')(611,612) + PumlItemImpl(duration)(612,620)(612,620) + PsiElement(PumlTokenType.IDENTIFIER)('duration')(612,620) + PsiElement(PumlTokenType.NEW_LINE_INDENT)('\n')(620,621) + PumlItemImpl(Bob)(621,624)(621,624) + PsiElement(PumlTokenType.IDENTIFIER)('Bob')(621,624) + PsiElement(PumlTokenType.WHITE_SPACE)(' ')(624,625) + PumlItemImpl(->)(625,627)(625,627) + PsiElement(PumlTokenType.OTHER)('->')(625,627) + PsiElement(PumlTokenType.WHITE_SPACE)(' ')(627,628) + PumlItemImpl(Max)(628,631)(628,631) + PsiElement(PumlTokenType.IDENTIFIER)('Max')(628,631) + PsiElement(PumlTokenType.WHITE_SPACE)(' ')(631,632) + PumlItemImpl(:)(632,633)(632,633) + PsiElement(PumlTokenType.OTHER)(':')(632,633) + PsiElement(PumlTokenType.WHITE_SPACE)(' ')(633,634) + PumlItemImpl(something)(634,643)(634,643) + PsiElement(PumlTokenType.IDENTIFIER)('something')(634,643) + PsiElement(PumlTokenType.NEW_LINE_INDENT)('\n')(643,644) + PumlItemImpl(Max)(644,647)(644,647) + PsiElement(PumlTokenType.IDENTIFIER)('Max')(644,647) + PsiElement(PumlTokenType.WHITE_SPACE)(' ')(647,648) + PumlItemImpl(->)(648,650)(648,650) + PsiElement(PumlTokenType.OTHER)('->')(648,650) + PsiElement(PumlTokenType.WHITE_SPACE)(' ')(650,651) + PumlItemImpl(Bob)(651,654)(651,654) + PsiElement(PumlTokenType.IDENTIFIER)('Bob')(651,654) + PsiElement(PumlTokenType.WHITE_SPACE)(' ')(654,655) + PumlItemImpl(:)(655,656)(655,656) + PsiElement(PumlTokenType.OTHER)(':')(655,656) + PsiElement(PumlTokenType.WHITE_SPACE)(' ')(656,657) + PumlItemImpl(something)(657,666)(657,666) + PsiElement(PumlTokenType.IDENTIFIER)('something')(657,666) + PsiElement(PumlTokenType.WHITE_SPACE)(' ')(666,667) + PumlItemImpl(else)(667,671)(667,671) + PsiElement(PumlTokenType.IDENTIFIER)('else')(667,671) + PsiElement(PumlTokenType.NEW_LINE_INDENT)('\n')(671,672) + PumlItemImpl({)(672,673)(672,673) + PsiElement(PumlTokenType.OTHER)('{')(672,673) + PumlItemImpl(end)(673,676)(673,676) + PsiElement(PumlTokenType.IDENTIFIER)('end')(673,676) + PumlItemImpl(})(676,677)(676,677) + PsiElement(PumlTokenType.OTHER)('}')(676,677) + PsiElement(PumlTokenType.WHITE_SPACE)(' ')(677,678) + PumlItemImpl(Bob)(678,681)(678,681) + PsiElement(PumlTokenType.IDENTIFIER)('Bob')(678,681) + PsiElement(PumlTokenType.WHITE_SPACE)(' ')(681,682) + PumlItemImpl(->)(682,684)(682,684) + PsiElement(PumlTokenType.OTHER)('->')(682,684) + PsiElement(PumlTokenType.WHITE_SPACE)(' ')(684,685) + PumlItemImpl(Alice)(685,690)(685,690) + PsiElement(PumlTokenType.IDENTIFIER)('Alice')(685,690) + PsiElement(PumlTokenType.WHITE_SPACE)(' ')(690,691) + PumlItemImpl(:)(691,692)(691,692) + PsiElement(PumlTokenType.OTHER)(':')(691,692) + PsiElement(PumlTokenType.WHITE_SPACE)(' ')(692,693) + PumlItemImpl(finish)(693,699)(693,699) + PsiElement(PumlTokenType.IDENTIFIER)('finish')(693,699) + PsiElement(PumlTokenType.NEW_LINE_INDENT)('\n\n')(699,701) + PumlItemImpl({)(701,702)(701,702) + PsiElement(PumlTokenType.OTHER)('{')(701,702) + PumlItemImpl(start)(702,707)(702,707) + PsiElement(PumlTokenType.IDENTIFIER)('start')(702,707) + PumlItemImpl(})(707,708)(707,708) + PsiElement(PumlTokenType.OTHER)('}')(707,708) + PsiElement(PumlTokenType.WHITE_SPACE)(' ')(708,709) + PumlItemImpl(<->)(709,712)(709,712) + PsiElement(PumlTokenType.OTHER)('<->')(709,712) + PsiElement(PumlTokenType.WHITE_SPACE)(' ')(712,713) + PumlItemImpl({)(713,714)(713,714) + PsiElement(PumlTokenType.OTHER)('{')(713,714) + PumlItemImpl(end)(714,717)(714,717) + PsiElement(PumlTokenType.IDENTIFIER)('end')(714,717) + PumlItemImpl(})(717,718)(717,718) + PsiElement(PumlTokenType.OTHER)('}')(717,718) + PsiElement(PumlTokenType.WHITE_SPACE)(' ')(718,719) + PumlItemImpl(:)(719,720)(719,720) + PsiElement(PumlTokenType.OTHER)(':')(719,720) + PsiElement(PumlTokenType.WHITE_SPACE)(' ')(720,721) + PumlItemImpl(some)(721,725)(721,725) + PsiElement(PumlTokenType.IDENTIFIER)('some')(721,725) + PsiElement(PumlTokenType.WHITE_SPACE)(' ')(725,726) + PumlItemImpl(time)(726,730)(726,730) + PsiElement(PumlTokenType.IDENTIFIER)('time')(726,730) + PsiElement(PumlTokenType.NEW_LINE_INDENT)('\n\n')(730,732) + PumlItemImpl(@enduml)(732,739)(732,739) + PsiElement(PumlTokenType.IDENTIFIER)('@enduml')(732,739) + PsiElement(PumlTokenType.WHITE_SPACE)(' ')(739,756) + PsiElement(PumlTokenType.NEW_LINE_INDENT)('\n')(756,757) + PsiElement(PumlTokenType.WHITE_SPACE)(' ')(757,762) + PsiElement(PumlTokenType.NEW_LINE_INDENT)('\n')(762,763) + PsiElement(PumlTokenType.WHITE_SPACE)(' ')(763,764) + PsiElement(PumlTokenType.NEW_LINE_INDENT)('\n')(764,765) + PumlItemImpl(@startuml)(765,774)(765,774) + PsiElement(PumlTokenType.IDENTIFIER)('@startuml')(765,774) + PsiElement(PumlTokenType.NEW_LINE_INDENT)('\n')(774,775) + PumlItemImpl(ProductOfferingPrice)(775,795)(775,795) + PsiElement(PumlTokenType.IDENTIFIER)('ProductOfferingPrice')(775,795) + PsiElement(PumlTokenType.WHITE_SPACE)(' ')(795,796) + PumlItemImpl(--)(796,798)(796,798) + PsiElement(PumlTokenType.OTHER)('--')(796,798) + PsiElement(PumlTokenType.WHITE_SPACE)(' ')(798,799) + PumlItemImpl(ProductUsageSpec)(799,815)(799,815) + PsiElement(PumlTokenType.IDENTIFIER)('ProductUsageSpec')(799,815) + PumlItemImpl(:)(815,816)(815,816) + PsiElement(PumlTokenType.OTHER)(':')(815,816) + PsiElement(PumlTokenType.WHITE_SPACE)(' ')(816,817) + PumlItemImpl(ForUsage)(817,825)(817,825) + PsiElement(PumlTokenType.IDENTIFIER)('ForUsage')(817,825) + PsiElement(PumlTokenType.WHITE_SPACE)(' ')(825,826) + PumlItemImpl(>)(826,827)(826,827) + PsiElement(PumlTokenType.OTHER)('>')(826,827) + PsiElement(PumlTokenType.NEW_LINE_INDENT)('\n')(827,828) + PumlItemImpl(()(828,829)(828,829) + PsiElement(PumlTokenType.OTHER)('(')(828,829) + PumlItemImpl(ProductOfferingPrice)(829,849)(829,849) + PsiElement(PumlTokenType.IDENTIFIER)('ProductOfferingPrice')(829,849) + PumlItemImpl(,)(849,850)(849,850) + PsiElement(PumlTokenType.OTHER)(',')(849,850) + PsiElement(PumlTokenType.WHITE_SPACE)(' ')(850,851) + PumlItemImpl(ProductUsageSpec)(851,867)(851,867) + PsiElement(PumlTokenType.IDENTIFIER)('ProductUsageSpec')(851,867) + PumlItemImpl())(867,868)(867,868) + PsiElement(PumlTokenType.OTHER)(')')(867,868) + PsiElement(PumlTokenType.WHITE_SPACE)(' ')(868,869) + PumlItemImpl(..)(869,871)(869,871) + PsiElement(PumlTokenType.OTHER)('..')(869,871) + PsiElement(PumlTokenType.WHITE_SPACE)(' ')(871,872) + PumlItemImpl(ProductUsageSpecCharacteristic)(872,902)(872,902) + PsiElement(PumlTokenType.IDENTIFIER)('ProductUsageSpecCharacteristic')(872,902) + PsiElement(PumlTokenType.NEW_LINE_INDENT)('\n')(902,903) + PumlItemImpl(@enduml)(903,910)(903,910) + PsiElement(PumlTokenType.IDENTIFIER)('@enduml')(903,910) \ No newline at end of file