diff --git a/.gitignore b/.gitignore index 0fee91d..8b2aa9c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ .idea/ *.iml out/ +*~ diff --git a/gen/com/oliverlockwood/plugins/jenkinsfile/JenkinsLexer.java b/gen/com/oliverlockwood/plugins/jenkinsfile/JenkinsLexer.java new file mode 100644 index 0000000..88a7af4 --- /dev/null +++ b/gen/com/oliverlockwood/plugins/jenkinsfile/JenkinsLexer.java @@ -0,0 +1,514 @@ +/* The following code was generated by JFlex 1.7.0-SNAPSHOT tweaked for IntelliJ platform */ + +package com.oliverlockwood.plugins.jenkinsfile; + +import com.intellij.lexer.FlexLexer; +import com.intellij.psi.tree.IElementType; +import com.oliverlockwood.plugins.jenkinsfile.psi.JenkinsTypes; +import com.intellij.psi.TokenType; + + +/** + * This class is a scanner generated by + * JFlex 1.7.0-SNAPSHOT + * from the specification file Jenkins.flex + */ +class JenkinsLexer 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 WAITING_PARAMETERS = 2; + + /** + * ZZ_LEXSTATE[l] is the state in the DFA for the lexical state l + * ZZ_LEXSTATE[l+1] is the state in the DFA for the lexical state l + * at the beginning of a line + * l is of the form l = 2*k, k a non negative integer + */ + private static final int ZZ_LEXSTATE[] = { + 0, 0, 1, 1 + }; + + /** + * Translates characters to character classes + * Chosen bits are [9, 6, 6] + * Total runtime size is 1568 bytes + */ + public static int ZZ_CMAP(int ch) { + return ZZ_CMAP_A[(ZZ_CMAP_Y[ZZ_CMAP_Z[ch>>12]|((ch>>6)&0x3f)]<<6)|(ch&0x3f)]; + } + + /* The ZZ_CMAP_Z table has 272 entries */ + static final char ZZ_CMAP_Z[] = zzUnpackCMap( + "\1\0\1\100\1\200\u010d\100"); + + /* The ZZ_CMAP_Y table has 192 entries */ + static final char ZZ_CMAP_Y[] = zzUnpackCMap( + "\1\0\1\1\1\2\175\3\1\4\77\3"); + + /* The ZZ_CMAP_A table has 320 entries */ + static final char ZZ_CMAP_A[] = zzUnpackCMap( + "\11\0\1\2\1\1\1\15\1\14\1\1\22\0\1\2\6\0\1\12\64\0\1\13\4\0\1\6\3\0\1\11\2"+ + "\0\1\4\3\0\1\10\3\0\1\5\1\0\1\7\1\3\21\0\1\15\242\0\2\15\26\0"); + + /** + * Translates DFA states to action switch labels. + */ + private static final int [] ZZ_ACTION = zzUnpackAction(); + + private static final String ZZ_ACTION_PACKED_0 = + "\2\0\1\1\1\2\2\1\1\3\1\1\1\4\3\0"+ + "\1\5\4\0"; + + private static int [] zzUnpackAction() { + int [] result = new int[17]; + 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\16\0\34\0\52\0\70\0\106\0\124\0\142"+ + "\0\34\0\160\0\176\0\214\0\34\0\232\0\250\0\266"+ + "\0\304"; + + private static int [] zzUnpackRowMap() { + int [] result = new int[17]; + int offset = 0; + offset = zzUnpackRowMap(ZZ_ROWMAP_PACKED_0, offset, result); + return result; + } + + private static int zzUnpackRowMap(String packed, int offset, int [] result) { + int i = 0; /* index in packed string */ + int j = offset; /* index in unpacked array */ + int l = packed.length(); + while (i < l) { + int high = packed.charAt(i++) << 16; + result[j++] = high | packed.charAt(i++); + } + return j; + } + + /** + * The transition table of the DFA + */ + private static final int [] ZZ_TRANS = zzUnpackTrans(); + + private static final String ZZ_TRANS_PACKED_0 = + "\1\3\2\4\1\5\1\3\1\6\6\3\1\4\1\0"+ + "\1\3\1\4\1\7\7\3\1\10\1\3\1\7\20\0"+ + "\2\4\11\0\1\4\5\0\1\11\17\0\1\12\10\0"+ + "\1\4\1\7\11\0\1\7\1\0\1\13\1\0\10\13"+ + "\3\0\1\13\7\0\1\14\6\0\1\13\1\0\10\13"+ + "\1\15\2\0\1\13\6\0\1\16\17\0\1\17\15\0"+ + "\1\20\16\0\1\21\14\0\1\11\5\0"; + + private static int [] zzUnpackTrans() { + int [] result = new int[210]; + 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\11\5\1\1\11\3\0\1\11\4\0"; + + private static int [] zzUnpackAttribute() { + int [] result = new int[17]; + 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; + + + /** + * Creates a new scanner + * + * @param in the java.io.Reader to read input from. + */ + JenkinsLexer(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 false, iff there was new input. + * + * @exception java.io.IOException if any I/O-Error occurs + */ + private boolean zzRefill() throws java.io.IOException { + return true; + } + + + /** + * Returns the current lexical state. + */ + public final int yystate() { + return zzLexicalState; + } + + + /** + * Enters a new lexical state + * + * @param newState the new lexical state + */ + public final void yybegin(int newState) { + zzLexicalState = newState; + } + + + /** + * Returns the text matched by the current regular expression. + */ + public final CharSequence yytext() { + return zzBuffer.subSequence(zzStartRead, zzMarkedPos); + } + + + /** + * Returns the character at position pos from the + * matched text. + * + * It is equivalent to yytext().charAt(pos), but faster + * + * @param pos the position of the character to fetch. + * A value from 0 to yylength()-1. + * + * @return the character at position pos + */ + public final char yycharat(int pos) { + return zzBuffer.charAt(zzStartRead+pos); + } + + + /** + * Returns the length of the matched text region. + */ + public final int yylength() { + return zzMarkedPos-zzStartRead; + } + + + /** + * Reports an error that occured while scanning. + * + * In a wellformed scanner (no or only correct usage of + * yypushback(int) and a match-all fallback rule) this method + * will only be called with things that "Can't Possibly Happen". + * If this method is called, something is seriously wrong + * (e.g. a JFlex bug producing a faulty scanner etc.). + * + * Usual syntax/scanner level error handling should be done + * in error fallback rules. + * + * @param errorCode the code of the errormessage to display + */ + private void zzScanError(int errorCode) { + String message; + try { + message = ZZ_ERROR_MSG[errorCode]; + } + catch (ArrayIndexOutOfBoundsException e) { + message = ZZ_ERROR_MSG[ZZ_UNKNOWN_ERROR]; + } + + throw new Error(message); + } + + + /** + * Pushes the specified amount of characters back into the input stream. + * + * They will be read again by then next call of the scanning method + * + * @param number the number of characters to be read again. + * This number must not be greater than yylength()! + */ + public void yypushback(int number) { + if ( number > yylength() ) + zzScanError(ZZ_PUSHBACK_2BIG); + + zzMarkedPos -= number; + } + + + /** + * Contains user EOF-code, which will be executed exactly once, + * when the end of file is reached + */ + private void zzDoEOF() { + if (!zzEOFDone) { + zzEOFDone = true; + + } + } + + + /** + * Resumes scanning until the next regular expression is matched, + * the end of input is encountered or an I/O-Error occurs. + * + * @return the next token + * @exception java.io.IOException if any I/O-Error occurs + */ + public IElementType advance() throws java.io.IOException { + int zzInput; + int zzAction; + + // cached fields: + int zzCurrentPosL; + int zzMarkedPosL; + int zzEndReadL = zzEndRead; + CharSequence zzBufferL = zzBuffer; + + 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; + zzDoEOF(); + return null; + } + else { + switch (zzAction < 0 ? zzAction : ZZ_ACTION[zzAction]) { + case 1: + { return TokenType.BAD_CHARACTER; + } + case 6: break; + case 2: + { yybegin(YYINITIAL); return TokenType.WHITE_SPACE; + } + case 7: break; + case 3: + { yybegin(WAITING_PARAMETERS); return TokenType.WHITE_SPACE; + } + case 8: break; + case 4: + { yybegin(WAITING_PARAMETERS); return JenkinsTypes.STEP_KEY; + } + case 9: break; + case 5: + { yybegin(YYINITIAL); return JenkinsTypes.PARAMETER; + } + case 10: break; + default: + zzScanError(ZZ_NO_MATCH); + } + } + } + } + + +} diff --git a/gen/com/oliverlockwood/plugins/jenkinsfile/parser/JenkinsParser.java b/gen/com/oliverlockwood/plugins/jenkinsfile/parser/JenkinsParser.java new file mode 100644 index 0000000..6563b7a --- /dev/null +++ b/gen/com/oliverlockwood/plugins/jenkinsfile/parser/JenkinsParser.java @@ -0,0 +1,88 @@ +// This is a generated file. Not intended for manual editing. +package com.oliverlockwood.plugins.jenkinsfile.parser; + +import com.intellij.lang.PsiBuilder; +import com.intellij.lang.PsiBuilder.Marker; +import static com.oliverlockwood.plugins.jenkinsfile.psi.JenkinsTypes.*; +import static com.intellij.lang.parser.GeneratedParserUtilBase.*; +import com.intellij.psi.tree.IElementType; +import com.intellij.lang.ASTNode; +import com.intellij.psi.tree.TokenSet; +import com.intellij.lang.PsiParser; +import com.intellij.lang.LightPsiParser; + +@SuppressWarnings({"SimplifiableIfStatement", "UnusedAssignment"}) +public class JenkinsParser implements PsiParser, LightPsiParser { + + public ASTNode parse(IElementType t, PsiBuilder b) { + parseLight(t, b); + return b.getTreeBuilt(); + } + + public void parseLight(IElementType t, PsiBuilder b) { + boolean r; + b = adapt_builder_(t, b, this, null); + Marker m = enter_section_(b, 0, _COLLAPSE_, null); + if (t == STEP) { + r = step(b, 0); + } + else { + r = parse_root_(t, b, 0); + } + exit_section_(b, 0, m, t, r, true, TRUE_CONDITION); + } + + protected boolean parse_root_(IElementType t, PsiBuilder b, int l) { + return jenkinsFile(b, l + 1); + } + + /* ********************************************************** */ + // step|CRLF|COMMENT + static boolean item_(PsiBuilder b, int l) { + if (!recursion_guard_(b, l, "item_")) return false; + boolean r; + Marker m = enter_section_(b); + r = step(b, l + 1); + if (!r) r = consumeToken(b, CRLF); + if (!r) r = consumeToken(b, COMMENT); + exit_section_(b, m, null, r); + return r; + } + + /* ********************************************************** */ + // item_* + static boolean jenkinsFile(PsiBuilder b, int l) { + if (!recursion_guard_(b, l, "jenkinsFile")) return false; + int c = current_position_(b); + while (true) { + if (!item_(b, l + 1)) break; + if (!empty_element_parsed_guard_(b, "jenkinsFile", c)) break; + c = current_position_(b); + } + return true; + } + + /* ********************************************************** */ + // (STEP_KEY PARAMETER) | STEP_KEY + public static boolean step(PsiBuilder b, int l) { + if (!recursion_guard_(b, l, "step")) return false; + if (!nextTokenIs(b, STEP_KEY)) return false; + boolean r; + Marker m = enter_section_(b); + r = step_0(b, l + 1); + if (!r) r = consumeToken(b, STEP_KEY); + exit_section_(b, m, STEP, r); + return r; + } + + // STEP_KEY PARAMETER + private static boolean step_0(PsiBuilder b, int l) { + if (!recursion_guard_(b, l, "step_0")) return false; + boolean r; + Marker m = enter_section_(b); + r = consumeTokens(b, 0, STEP_KEY, PARAMETER); + exit_section_(b, m, null, r); + return r; + } + +} diff --git a/gen/com/oliverlockwood/plugins/jenkinsfile/psi/JenkinsStep.java b/gen/com/oliverlockwood/plugins/jenkinsfile/psi/JenkinsStep.java new file mode 100644 index 0000000..93a6c4d --- /dev/null +++ b/gen/com/oliverlockwood/plugins/jenkinsfile/psi/JenkinsStep.java @@ -0,0 +1,10 @@ +// This is a generated file. Not intended for manual editing. +package com.oliverlockwood.plugins.jenkinsfile.psi; + +import java.util.List; +import org.jetbrains.annotations.*; +import com.intellij.psi.PsiElement; + +public interface JenkinsStep extends PsiElement { + +} diff --git a/gen/com/oliverlockwood/plugins/jenkinsfile/psi/JenkinsTypes.java b/gen/com/oliverlockwood/plugins/jenkinsfile/psi/JenkinsTypes.java new file mode 100644 index 0000000..2eedf1b --- /dev/null +++ b/gen/com/oliverlockwood/plugins/jenkinsfile/psi/JenkinsTypes.java @@ -0,0 +1,27 @@ +// This is a generated file. Not intended for manual editing. +package com.oliverlockwood.plugins.jenkinsfile.psi; + +import com.intellij.psi.tree.IElementType; +import com.intellij.psi.PsiElement; +import com.intellij.lang.ASTNode; +import com.oliverlockwood.plugins.jenkinsfile.psi.impl.*; + +public interface JenkinsTypes { + + IElementType STEP = new JenkinsElementType("STEP"); + + IElementType COMMENT = new JenkinsTokenType("COMMENT"); + IElementType CRLF = new JenkinsTokenType("CRLF"); + IElementType PARAMETER = new JenkinsTokenType("PARAMETER"); + IElementType STEP_KEY = new JenkinsTokenType("STEP_KEY"); + + class Factory { + public static PsiElement createElement(ASTNode node) { + IElementType type = node.getElementType(); + if (type == STEP) { + return new JenkinsStepImpl(node); + } + throw new AssertionError("Unknown element type: " + type); + } + } +} diff --git a/gen/com/oliverlockwood/plugins/jenkinsfile/psi/JenkinsVisitor.java b/gen/com/oliverlockwood/plugins/jenkinsfile/psi/JenkinsVisitor.java new file mode 100644 index 0000000..fe451e3 --- /dev/null +++ b/gen/com/oliverlockwood/plugins/jenkinsfile/psi/JenkinsVisitor.java @@ -0,0 +1,18 @@ +// This is a generated file. Not intended for manual editing. +package com.oliverlockwood.plugins.jenkinsfile.psi; + +import org.jetbrains.annotations.*; +import com.intellij.psi.PsiElementVisitor; +import com.intellij.psi.PsiElement; + +public class JenkinsVisitor extends PsiElementVisitor { + + public void visitStep(@NotNull JenkinsStep o) { + visitPsiElement(o); + } + + public void visitPsiElement(@NotNull PsiElement o) { + visitElement(o); + } + +} diff --git a/gen/com/oliverlockwood/plugins/jenkinsfile/psi/impl/JenkinsStepImpl.java b/gen/com/oliverlockwood/plugins/jenkinsfile/psi/impl/JenkinsStepImpl.java new file mode 100644 index 0000000..f8e7352 --- /dev/null +++ b/gen/com/oliverlockwood/plugins/jenkinsfile/psi/impl/JenkinsStepImpl.java @@ -0,0 +1,29 @@ +// This is a generated file. Not intended for manual editing. +package com.oliverlockwood.plugins.jenkinsfile.psi.impl; + +import java.util.List; +import org.jetbrains.annotations.*; +import com.intellij.lang.ASTNode; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiElementVisitor; +import com.intellij.psi.util.PsiTreeUtil; +import static com.oliverlockwood.plugins.jenkinsfile.psi.JenkinsTypes.*; +import com.intellij.extapi.psi.ASTWrapperPsiElement; +import com.oliverlockwood.plugins.jenkinsfile.psi.*; + +public class JenkinsStepImpl extends ASTWrapperPsiElement implements JenkinsStep { + + public JenkinsStepImpl(ASTNode node) { + super(node); + } + + public void accept(@NotNull JenkinsVisitor visitor) { + visitor.visitStep(this); + } + + public void accept(@NotNull PsiElementVisitor visitor) { + if (visitor instanceof JenkinsVisitor) accept((JenkinsVisitor)visitor); + else super.accept(visitor); + } + +} diff --git a/resources/META-INF/plugin.xml b/resources/META-INF/plugin.xml index 7bd664e..b54ff84 100644 --- a/resources/META-INF/plugin.xml +++ b/resources/META-INF/plugin.xml @@ -28,6 +28,7 @@ + diff --git a/src/com/oliverlockwood/plugins/jenkinsfile/Jenkins.bnf b/src/com/oliverlockwood/plugins/jenkinsfile/Jenkins.bnf new file mode 100644 index 0000000..ecf3d2a --- /dev/null +++ b/src/com/oliverlockwood/plugins/jenkinsfile/Jenkins.bnf @@ -0,0 +1,20 @@ +{ + parserClass="com.oliverlockwood.plugins.jenkinsfile.parser.JenkinsParser" + + extends="com.intellij.extapi.psi.ASTWrapperPsiElement" + + psiClassPrefix="Jenkins" + psiImplClassSuffix="Impl" + psiPackage="com.oliverlockwood.plugins.jenkinsfile.psi" + psiImplPackage="com.oliverlockwood.plugins.jenkinsfile.psi.impl" + + elementTypeHolderClass="com.oliverlockwood.plugins.jenkinsfile.psi.JenkinsTypes" + elementTypeClass="com.oliverlockwood.plugins.jenkinsfile.psi.JenkinsElementType" + tokenTypeClass="com.oliverlockwood.plugins.jenkinsfile.psi.JenkinsTokenType" +} + +jenkinsFile ::= item_* + +private item_ ::= (step|CRLF|COMMENT) + +step ::= (STEP_KEY PARAMETER) | STEP_KEY diff --git a/src/com/oliverlockwood/plugins/jenkinsfile/Jenkins.flex b/src/com/oliverlockwood/plugins/jenkinsfile/Jenkins.flex new file mode 100644 index 0000000..33c3651 --- /dev/null +++ b/src/com/oliverlockwood/plugins/jenkinsfile/Jenkins.flex @@ -0,0 +1,49 @@ +package com.oliverlockwood.plugins.jenkinsfile; + +import com.intellij.lexer.FlexLexer; +import com.intellij.psi.tree.IElementType; +import com.oliverlockwood.plugins.jenkinsfile.psi.JenkinsTypes; +import com.intellij.psi.TokenType; + +%% + +%class JenkinsLexer +%implements FlexLexer +%unicode +%function advance +%type IElementType +%eof{ return; +%eof} + +CRLF= \n|\r|\r\n +WHITESPACE=[\ \t\f] +STEP_NAME="sh" | "parallel" +SQ="'" +SQ_PARAMETER_CHAR=[^\n\r\f\\'] +TQ="'''" +//TQ_PARAMETER='(?:.|\n)+ +//END_OF_LINE_COMMENT=("#"|"!")[^\r\n]* + +%state WAITING_PARAMETERS + +%% + +// {END_OF_LINE_COMMENT} { yybegin(YYINITIAL); return JenkinsTypes.COMMENT; } + + {STEP_NAME} { yybegin(WAITING_PARAMETERS); return JenkinsTypes.STEP_KEY; } + +// {SEPARATOR} { yybegin(WAITING_VALUE); return JenkinsTypes.SEPARATOR; } + + {CRLF}({CRLF}|{WHITESPACE})+ { yybegin(YYINITIAL); return TokenType.WHITE_SPACE; } + + {WHITESPACE}+ { yybegin(WAITING_PARAMETERS); return TokenType.WHITE_SPACE; } + +// {TQ}(?:.|\n)+?{TQ} { yybegin(YYINITIAL); return JenkinsTypes.PARAMETER; } + + {SQ}{SQ_PARAMETER_CHAR}+{SQ} { yybegin(YYINITIAL); return JenkinsTypes.PARAMETER; } + +({CRLF}|{WHITESPACE})+ { yybegin(YYINITIAL); return TokenType.WHITE_SPACE; } + +{WHITESPACE}+ { yybegin(YYINITIAL); return TokenType.WHITE_SPACE; } + +. { return TokenType.BAD_CHARACTER; } diff --git a/src/com/oliverlockwood/plugins/jenkinsfile/JenkinsLexerAdapter.java b/src/com/oliverlockwood/plugins/jenkinsfile/JenkinsLexerAdapter.java new file mode 100644 index 0000000..e714275 --- /dev/null +++ b/src/com/oliverlockwood/plugins/jenkinsfile/JenkinsLexerAdapter.java @@ -0,0 +1,11 @@ +package com.oliverlockwood.plugins.jenkinsfile; + +import com.intellij.lexer.FlexAdapter; + +import java.io.Reader; + +public class JenkinsLexerAdapter extends FlexAdapter { + public JenkinsLexerAdapter() { + super(new JenkinsLexer((Reader) null)); + } +} diff --git a/src/com/oliverlockwood/plugins/jenkinsfile/JenkinsParserDefinition.java b/src/com/oliverlockwood/plugins/jenkinsfile/JenkinsParserDefinition.java new file mode 100644 index 0000000..4afb2d9 --- /dev/null +++ b/src/com/oliverlockwood/plugins/jenkinsfile/JenkinsParserDefinition.java @@ -0,0 +1,62 @@ +package com.oliverlockwood.plugins.jenkinsfile; + +import com.intellij.lang.*; +import com.intellij.lexer.Lexer; +import com.intellij.openapi.project.Project; +import com.intellij.psi.*; +import com.intellij.psi.tree.*; +import com.oliverlockwood.plugins.jenkinsfile.parser.JenkinsParser; +import com.oliverlockwood.plugins.jenkinsfile.psi.*; +import org.jetbrains.annotations.NotNull; + +public class JenkinsParserDefinition implements ParserDefinition { + public static final TokenSet WHITE_SPACES = TokenSet.create(TokenType.WHITE_SPACE); + public static final TokenSet COMMENTS = TokenSet.create(JenkinsTypes.COMMENT); + + public static final IFileElementType FILE = + new IFileElementType(Language.findInstance(JenkinsLanguage.class)); + + @NotNull + @Override + public Lexer createLexer(Project project) { + return new JenkinsLexerAdapter(); + } + + @NotNull + public TokenSet getWhitespaceTokens() { + return WHITE_SPACES; + } + + @NotNull + public TokenSet getCommentTokens() { + return COMMENTS; + } + + @NotNull + public TokenSet getStringLiteralElements() { + return TokenSet.EMPTY; + } + + @NotNull + public PsiParser createParser(final Project project) { + return new JenkinsParser(); + } + + @Override + public IFileElementType getFileNodeType() { + return FILE; + } + + public PsiFile createFile(FileViewProvider viewProvider) { + return new JenkinsFile(viewProvider); + } + + public SpaceRequirements spaceExistanceTypeBetweenTokens(ASTNode left, ASTNode right) { + return SpaceRequirements.MAY; + } + + @NotNull + public PsiElement createElement(ASTNode node) { + return JenkinsTypes.Factory.createElement(node); + } +} \ No newline at end of file diff --git a/src/com/oliverlockwood/plugins/jenkinsfile/psi/JenkinsElementType.java b/src/com/oliverlockwood/plugins/jenkinsfile/psi/JenkinsElementType.java new file mode 100644 index 0000000..0291188 --- /dev/null +++ b/src/com/oliverlockwood/plugins/jenkinsfile/psi/JenkinsElementType.java @@ -0,0 +1,11 @@ +package com.oliverlockwood.plugins.jenkinsfile.psi; + +import com.intellij.psi.tree.IElementType; +import com.oliverlockwood.plugins.jenkinsfile.JenkinsLanguage; +import org.jetbrains.annotations.*; + +public class JenkinsElementType extends IElementType { + public JenkinsElementType(@NotNull @NonNls String debugName) { + super(debugName, JenkinsLanguage.INSTANCE); + } +} \ No newline at end of file diff --git a/src/com/oliverlockwood/plugins/jenkinsfile/psi/JenkinsFile.java b/src/com/oliverlockwood/plugins/jenkinsfile/psi/JenkinsFile.java new file mode 100644 index 0000000..d12cafa --- /dev/null +++ b/src/com/oliverlockwood/plugins/jenkinsfile/psi/JenkinsFile.java @@ -0,0 +1,31 @@ +package com.oliverlockwood.plugins.jenkinsfile.psi; + +import com.intellij.extapi.psi.PsiFileBase; +import com.intellij.openapi.fileTypes.FileType; +import com.intellij.psi.FileViewProvider; +import com.oliverlockwood.plugins.jenkinsfile.*; +import org.jetbrains.annotations.NotNull; + +import javax.swing.*; + +public class JenkinsFile extends PsiFileBase { + public JenkinsFile(@NotNull FileViewProvider viewProvider) { + super(viewProvider, JenkinsLanguage.INSTANCE); + } + + @NotNull + @Override + public FileType getFileType() { + return JenkinsFileType.INSTANCE; + } + + @Override + public String toString() { + return "Jenkins File"; + } + + @Override + public Icon getIcon(int flags) { + return super.getIcon(flags); + } +} \ No newline at end of file diff --git a/src/com/oliverlockwood/plugins/jenkinsfile/psi/JenkinsTokenType.java b/src/com/oliverlockwood/plugins/jenkinsfile/psi/JenkinsTokenType.java new file mode 100644 index 0000000..0149c12 --- /dev/null +++ b/src/com/oliverlockwood/plugins/jenkinsfile/psi/JenkinsTokenType.java @@ -0,0 +1,16 @@ +package com.oliverlockwood.plugins.jenkinsfile.psi; + +import com.intellij.psi.tree.IElementType; +import com.oliverlockwood.plugins.jenkinsfile.JenkinsLanguage; +import org.jetbrains.annotations.*; + +public class JenkinsTokenType extends IElementType { + public JenkinsTokenType(@NotNull @NonNls String debugName) { + super(debugName, JenkinsLanguage.INSTANCE); + } + + @Override + public String toString() { + return "JenkinsTokenType." + super.toString(); + } +} \ No newline at end of file