From 3eac47e013c6bd1e38e9c1014ec4bb5b5f25c299 Mon Sep 17 00:00:00 2001 From: Quentin Ligier Date: Tue, 1 Oct 2024 20:55:01 +0200 Subject: [PATCH] Add support for the ig.ini file --- CHANGELOG.md | 4 + build.gradle.kts | 2 + .../plugin/fss/igini/language/IniLexer.java | 585 ++++++++++++++++++ .../plugin/fss/igini/IgIniAnnotator.java | 109 ++++ .../fss/igini/IgIniCompletionContributor.java | 96 +++ .../plugin/fss/igini/IgIniFileType.java | 60 ++ .../plugin/fss/igini/IgIniLanguage.java | 19 + .../plugin/fss/igini/IgIniSpecs.java | 21 + .../fss/igini/language/IniCommenter.java | 51 ++ .../highlighter/IniSyntaxHighlighter.java | 78 +++ .../IniSyntaxHighlighterFactory.java | 28 + .../igini/language/lexer/IniLexerAdapter.java | 18 + .../plugin/fss/igini/language/lexer/ini.flex | 55 ++ .../fss/igini/language/parser/IniParser.java | 153 +++++ .../language/parser/IniParserDefinition.java | 74 +++ .../igini/language/parser/IniParserUtil.java | 13 + .../plugin/fss/igini/language/parser/ini.bnf | 33 + .../igini/language/psi/IniElementType.java | 20 + .../fss/igini/language/psi/IniFile.java | 43 ++ .../plugin/fss/igini/language/psi/IniKey.java | 10 + .../fss/igini/language/psi/IniProperty.java | 20 + .../fss/igini/language/psi/IniSection.java | 17 + .../fss/igini/language/psi/IniTokenSets.java | 15 + .../fss/igini/language/psi/IniTokenType.java | 25 + .../fss/igini/language/psi/IniTypes.java | 40 ++ .../fss/igini/language/psi/IniValue.java | 10 + .../fss/igini/language/psi/IniVisitor.java | 30 + .../igini/language/psi/impl/IniKeyImpl.java | 30 + .../language/psi/impl/IniPropertyImpl.java | 52 ++ .../language/psi/impl/IniPsiImplUtil.java | 53 ++ .../language/psi/impl/IniSectionImpl.java | 46 ++ .../igini/language/psi/impl/IniValueImpl.java | 30 + src/main/resources/META-INF/plugin.xml | 24 + 33 files changed, 1864 insertions(+) create mode 100644 src/main/gen/ch/qligier/jetbrains/plugin/fss/igini/language/IniLexer.java create mode 100644 src/main/java/ch/qligier/jetbrains/plugin/fss/igini/IgIniAnnotator.java create mode 100644 src/main/java/ch/qligier/jetbrains/plugin/fss/igini/IgIniCompletionContributor.java create mode 100644 src/main/java/ch/qligier/jetbrains/plugin/fss/igini/IgIniFileType.java create mode 100644 src/main/java/ch/qligier/jetbrains/plugin/fss/igini/IgIniLanguage.java create mode 100644 src/main/java/ch/qligier/jetbrains/plugin/fss/igini/IgIniSpecs.java create mode 100644 src/main/java/ch/qligier/jetbrains/plugin/fss/igini/language/IniCommenter.java create mode 100644 src/main/java/ch/qligier/jetbrains/plugin/fss/igini/language/highlighter/IniSyntaxHighlighter.java create mode 100644 src/main/java/ch/qligier/jetbrains/plugin/fss/igini/language/highlighter/IniSyntaxHighlighterFactory.java create mode 100644 src/main/java/ch/qligier/jetbrains/plugin/fss/igini/language/lexer/IniLexerAdapter.java create mode 100644 src/main/java/ch/qligier/jetbrains/plugin/fss/igini/language/lexer/ini.flex create mode 100644 src/main/java/ch/qligier/jetbrains/plugin/fss/igini/language/parser/IniParser.java create mode 100644 src/main/java/ch/qligier/jetbrains/plugin/fss/igini/language/parser/IniParserDefinition.java create mode 100644 src/main/java/ch/qligier/jetbrains/plugin/fss/igini/language/parser/IniParserUtil.java create mode 100644 src/main/java/ch/qligier/jetbrains/plugin/fss/igini/language/parser/ini.bnf create mode 100644 src/main/java/ch/qligier/jetbrains/plugin/fss/igini/language/psi/IniElementType.java create mode 100644 src/main/java/ch/qligier/jetbrains/plugin/fss/igini/language/psi/IniFile.java create mode 100644 src/main/java/ch/qligier/jetbrains/plugin/fss/igini/language/psi/IniKey.java create mode 100644 src/main/java/ch/qligier/jetbrains/plugin/fss/igini/language/psi/IniProperty.java create mode 100644 src/main/java/ch/qligier/jetbrains/plugin/fss/igini/language/psi/IniSection.java create mode 100644 src/main/java/ch/qligier/jetbrains/plugin/fss/igini/language/psi/IniTokenSets.java create mode 100644 src/main/java/ch/qligier/jetbrains/plugin/fss/igini/language/psi/IniTokenType.java create mode 100644 src/main/java/ch/qligier/jetbrains/plugin/fss/igini/language/psi/IniTypes.java create mode 100644 src/main/java/ch/qligier/jetbrains/plugin/fss/igini/language/psi/IniValue.java create mode 100644 src/main/java/ch/qligier/jetbrains/plugin/fss/igini/language/psi/IniVisitor.java create mode 100644 src/main/java/ch/qligier/jetbrains/plugin/fss/igini/language/psi/impl/IniKeyImpl.java create mode 100644 src/main/java/ch/qligier/jetbrains/plugin/fss/igini/language/psi/impl/IniPropertyImpl.java create mode 100644 src/main/java/ch/qligier/jetbrains/plugin/fss/igini/language/psi/impl/IniPsiImplUtil.java create mode 100644 src/main/java/ch/qligier/jetbrains/plugin/fss/igini/language/psi/impl/IniSectionImpl.java create mode 100644 src/main/java/ch/qligier/jetbrains/plugin/fss/igini/language/psi/impl/IniValueImpl.java diff --git a/CHANGELOG.md b/CHANGELOG.md index b451035..bfa5cf6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added + +- Add support for the ig.ini file: syntax highlighting, annotator, completion and some other features + ### Fixed - Replace a non-fixed width lookbehind with an atomic group ([#1](https://github.com/qligier/fsh.tmbundle/issues/1)) diff --git a/build.gradle.kts b/build.gradle.kts index a50c70a..78f8357 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -18,6 +18,8 @@ plugins { group = providers.gradleProperty("pluginGroup").get() version = providers.gradleProperty("pluginVersion").get() +sourceSets["main"].java.srcDirs("src/main/gen") + // Set the JVM language level used to build the project. kotlin { jvmToolchain(17) diff --git a/src/main/gen/ch/qligier/jetbrains/plugin/fss/igini/language/IniLexer.java b/src/main/gen/ch/qligier/jetbrains/plugin/fss/igini/language/IniLexer.java new file mode 100644 index 0000000..ea06a15 --- /dev/null +++ b/src/main/gen/ch/qligier/jetbrains/plugin/fss/igini/language/IniLexer.java @@ -0,0 +1,585 @@ +// Generated by JFlex 1.9.1 http://jflex.de/ (tweaked for IntelliJ platform) +// source: ini.flex + +// Copyright 2024 Quentin Ligier. Use of this source code is governed by the MIT license. +// A lexer for the INI file format, used to parse the ig.ini file. +// See https://plugins.jetbrains.com/docs/intellij/lexer-and-parser-definition.html +// See https://plugins.jetbrains.com/docs/intellij/implementing-lexer.html +package ch.qligier.jetbrains.plugin.fss.igini.language; + +import com.intellij.lexer.FlexLexer; +import com.intellij.psi.tree.IElementType; +import ch.qligier.jetbrains.plugin.fss.igini.language.psi.IniTypes; +import com.intellij.psi.TokenType; + + +public class IniLexer 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 IN_VALUE = 2; + public static final int IN_KEY_VALUE_SEPARATOR = 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 + }; + + /** + * Top-level table for translating characters to character classes + */ + private static final int [] ZZ_CMAP_TOP = zzUnpackcmap_top(); + + private static final String ZZ_CMAP_TOP_PACKED_0 = + "\1\0\37\u0100\1\u0200\267\u0100\10\u0300\u1020\u0100"; + + private static int [] zzUnpackcmap_top() { + int [] result = new int[4352]; + int offset = 0; + offset = zzUnpackcmap_top(ZZ_CMAP_TOP_PACKED_0, offset, result); + return result; + } + + private static int zzUnpackcmap_top(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; + } + + + /** + * Second-level tables for translating characters to character classes + */ + private static final int [] ZZ_CMAP_BLOCKS = zzUnpackcmap_blocks(); + + private static final String ZZ_CMAP_BLOCKS_PACKED_0 = + "\11\0\1\1\1\2\1\3\1\1\1\4\22\0\1\5"+ + "\2\0\1\6\27\0\1\6\1\0\1\7\35\0\1\10"+ + "\1\0\1\11\47\0\1\3\u01a2\0\2\3\326\0\u0100\3"; + + private static int [] zzUnpackcmap_blocks() { + int [] result = new int[1024]; + int offset = 0; + offset = zzUnpackcmap_blocks(ZZ_CMAP_BLOCKS_PACKED_0, offset, result); + return result; + } + + private static int zzUnpackcmap_blocks(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 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\2\2\1\3\1\4\1\1\1\5\2\6"+ + "\1\5\1\7\1\4\1\1\1\0\1\5\3\10"; + + private static int [] zzUnpackAction() { + int [] result = new int[21]; + 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\12\0\24\0\36\0\50\0\62\0\74\0\106"+ + "\0\120\0\132\0\106\0\144\0\156\0\106\0\170\0\202"+ + "\0\214\0\226\0\36\0\106\0\132"; + + private static int [] zzUnpackRowMap() { + int [] result = new int[21]; + 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() - 1; + 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\4\1\6\1\5\1\7\1\10"+ + "\1\11\1\4\1\12\1\5\1\13\1\12\1\14\1\5"+ + "\1\7\1\10\1\15\1\12\1\10\1\5\1\13\1\0"+ + "\1\14\1\5\1\7\1\16\1\17\1\10\1\4\2\0"+ + "\1\4\2\0\1\4\1\0\2\4\1\0\1\5\3\0"+ + "\1\5\6\0\1\6\1\0\1\6\5\0\2\7\1\0"+ + "\1\7\1\0\5\7\12\0\1\20\2\21\1\20\2\21"+ + "\1\20\1\21\1\20\1\4\1\12\2\0\1\12\1\0"+ + "\2\12\1\0\2\12\2\0\1\13\7\0\1\22\2\21"+ + "\1\22\1\21\2\22\1\21\1\22\1\12\11\21\1\0"+ + "\1\20\2\21\1\20\2\21\1\20\1\21\1\20\1\23"+ + "\11\21\1\24\1\22\2\21\1\22\1\21\2\22\1\21"+ + "\1\22\1\25"; + + private static int [] zzUnpacktrans() { + int [] result = new int[160]; + 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 {@code aState} + */ + private static final int [] ZZ_ATTRIBUTE = zzUnpackAttribute(); + + private static final String ZZ_ATTRIBUTE_PACKED_0 = + "\3\0\4\1\1\11\2\1\1\11\2\1\1\11\2\1"+ + "\1\0\2\1\1\11\1\1"; + + private static int [] zzUnpackAttribute() { + int [] result = new int[21]; + 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; + + /** zzAtEOF == true <=> the scanner is at the EOF */ + private boolean zzAtEOF; + + /** Number of newlines encountered up to the start of the matched text. */ + @SuppressWarnings("unused") + private int yyline; + + /** Number of characters from the last newline up to the start of the matched text. */ + @SuppressWarnings("unused") + protected int yycolumn; + + /** Number of characters up to the start of the matched text. */ + @SuppressWarnings("unused") + private long yychar; + + /** Whether the scanner is currently at the beginning of a line. */ + @SuppressWarnings("unused") + private boolean zzAtBOL = true; + + /** Whether 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. + */ + public IniLexer(java.io.Reader in) { + this.zzReader = in; + } + + + /** Returns the maximum size of the scanner buffer, which limits the size of tokens. */ + private int zzMaxBufferLen() { + return Integer.MAX_VALUE; + } + + /** Whether the scanner buffer can grow to accommodate a larger token. */ + private boolean zzCanGrow() { + return true; + } + + /** + * Translates raw input code points to DFA table row + */ + private static int zzCMap(int input) { + int offset = input & 255; + return offset == input ? ZZ_CMAP_BLOCKS[offset] : ZZ_CMAP_BLOCKS[ZZ_CMAP_TOP[input >> 8] | offset]; + } + + 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; + } + + + /** + * 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); + 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); + zzCurrentPosL += Character.charCount(zzInput); + } + } + int zzNext = zzTransL[ zzRowMapL[zzState] + zzCMap(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: + { yybegin(IN_KEY_VALUE_SEPARATOR); return IniTypes.PKEY; + } + // fall through + case 9: break; + case 2: + { return TokenType.WHITE_SPACE; + } + // fall through + case 10: break; + case 3: + { return IniTypes.COMMENT; + } + // fall through + case 11: break; + case 4: + { return TokenType.BAD_CHARACTER; + } + // fall through + case 12: break; + case 5: + { return IniTypes.PVALUE; + } + // fall through + case 13: break; + case 6: + { yybegin(YYINITIAL); return TokenType.WHITE_SPACE; + } + // fall through + case 14: break; + case 7: + { yybegin(IN_VALUE); return IniTypes.EQUAL; + } + // fall through + case 15: break; + case 8: + { return IniTypes.SECTION_NAME; + } + // fall through + case 16: break; + default: + zzScanError(ZZ_NO_MATCH); + } + } + } + } + + +} diff --git a/src/main/java/ch/qligier/jetbrains/plugin/fss/igini/IgIniAnnotator.java b/src/main/java/ch/qligier/jetbrains/plugin/fss/igini/IgIniAnnotator.java new file mode 100644 index 0000000..a9dab2e --- /dev/null +++ b/src/main/java/ch/qligier/jetbrains/plugin/fss/igini/IgIniAnnotator.java @@ -0,0 +1,109 @@ +// Copyright 2024 Quentin Ligier. Use of this source code is governed by the MIT license. + +package ch.qligier.jetbrains.plugin.fss.igini; + +import ch.qligier.jetbrains.plugin.fss.igini.language.psi.IniFile; +import ch.qligier.jetbrains.plugin.fss.igini.language.psi.IniProperty; +import ch.qligier.jetbrains.plugin.fss.igini.language.psi.IniSection; +import com.intellij.lang.annotation.AnnotationHolder; +import com.intellij.lang.annotation.Annotator; +import com.intellij.lang.annotation.HighlightSeverity; +import com.intellij.psi.PsiElement; +import org.jetbrains.annotations.NotNull; + +/** + * An annotator for the ig.ini file. + * + * @author Quentin Ligier + * @see Annotator + **/ +public class IgIniAnnotator implements Annotator { + + /** + * Annotates the specified PSI element. It is guaranteed to be executed in non-reentrant fashion. I.e there will be + * no call of this method for this instance before previous call get completed. Multiple instances of the annotator + * might exist simultaneously, though. + * + * @param element to annotate. + * @param holder the container which receives annotations created by the plugin. + */ + @Override + public void annotate(@NotNull final PsiElement element, @NotNull final AnnotationHolder holder) { + if (!(element instanceof final IniFile file)) { + return; + } + + // Properties outside a section aren't supported by the IG Publisher + for (final IniProperty property : file.getProperties()) { + this.createWarningForUnsupportedProperty(holder, property); + } + + boolean section_ig_found = false; + for (final IniSection section : file.getSections()) { + if (!IgIniSpecs.IG_SECTION_NAME.equals(section.getSectionName())) { + final var target = section.getFirstChild(); + holder.newAnnotation(HighlightSeverity.WEAK_WARNING, + String.format("The section '%s' is not supported by the IG Publisher", + section.getSectionName())) + .range(target != null ? target : section) + .create(); + continue; + } + + section_ig_found = true; + + boolean property_template_found = false; + boolean property_ig_found = false; + for (final IniProperty property : section.getProperties()) { + final var keyName = property.getKeyName(); + if (keyName == null) { + continue; + } + switch (keyName) { + case IgIniSpecs.TEMPLATE_KEY_NAME: + property_template_found = true; + break; + case IgIniSpecs.IG_KEY_NAME: + property_ig_found = true; + break; + default: + this.createWarningForUnsupportedProperty(holder, property); + break; + } + } + + if (!property_ig_found) { + holder.newAnnotation(HighlightSeverity.ERROR, + String.format("The property '%s' is not defined in the file", + IgIniSpecs.IG_KEY_NAME)) + .range(section.getFirstChild()) + .create(); + } + if (!property_template_found) { + holder.newAnnotation(HighlightSeverity.ERROR, + String.format("The property '%s' is not defined in the file", + IgIniSpecs.TEMPLATE_KEY_NAME)) + .range(section.getFirstChild()) + .create(); + } + } + + if (!section_ig_found) { + holder.newAnnotation(HighlightSeverity.ERROR, + String.format("The section '%s' is not defined in the file", + IgIniSpecs.IG_SECTION_NAME)) + .range(file) + .create(); + } + } + + private void createWarningForUnsupportedProperty(final @NotNull AnnotationHolder holder, + final @NotNull IniProperty property) { + final PsiElement keyElement = property.getKeyElement(); + holder.newAnnotation(HighlightSeverity.WEAK_WARNING, + String.format("The property '%s' is not supported by the IG Publisher", + property.getKeyName())) + .range(keyElement != null ? keyElement : property) + .create(); + } +} diff --git a/src/main/java/ch/qligier/jetbrains/plugin/fss/igini/IgIniCompletionContributor.java b/src/main/java/ch/qligier/jetbrains/plugin/fss/igini/IgIniCompletionContributor.java new file mode 100644 index 0000000..e7967fe --- /dev/null +++ b/src/main/java/ch/qligier/jetbrains/plugin/fss/igini/IgIniCompletionContributor.java @@ -0,0 +1,96 @@ +// Copyright 2024 Quentin Ligier. Use of this source code is governed by the MIT license. + +package ch.qligier.jetbrains.plugin.fss.igini; + +import ch.qligier.jetbrains.plugin.fss.igini.language.psi.IniProperty; +import ch.qligier.jetbrains.plugin.fss.igini.language.psi.IniSection; +import ch.qligier.jetbrains.plugin.fss.igini.language.psi.IniTypes; +import com.intellij.codeInsight.completion.*; +import com.intellij.codeInsight.lookup.LookupElementBuilder; +import com.intellij.patterns.PlatformPatterns; +import com.intellij.psi.PsiElement; +import com.intellij.psi.impl.source.tree.CompositeElement; +import com.intellij.psi.impl.source.tree.LeafPsiElement; +import com.intellij.util.ProcessingContext; +import org.jetbrains.annotations.NotNull; + +/** + * A contributor that provides completion variants for the ig.ini file. + * + * @author Quentin Ligier + * @see Completion Contributor + **/ +public class IgIniCompletionContributor extends CompletionContributor { + + public IgIniCompletionContributor() { + // Provide completion for keys + this.extend(CompletionType.BASIC, + PlatformPatterns.psiElement(IniTypes.PKEY), + new CompletionProvider<>() { + public void addCompletions(@NotNull CompletionParameters parameters, + @NotNull ProcessingContext context, + @NotNull CompletionResultSet resultSet) { + resultSet.addElement(LookupElementBuilder.create(IgIniSpecs.IG_KEY_NAME)); + resultSet.addElement(LookupElementBuilder.create(IgIniSpecs.TEMPLATE_KEY_NAME)); + } + } + ); + + // Provide completion for values + this.extend(CompletionType.BASIC, + PlatformPatterns.psiElement(IniTypes.PVALUE), + new CompletionProvider<>() { + public void addCompletions(@NotNull CompletionParameters parameters, + @NotNull ProcessingContext context, + @NotNull CompletionResultSet resultSet) { + if (parameters.getPosition() instanceof final LeafPsiElement leaf) { + if (leaf.getElementType() != IniTypes.PVALUE) { + return; + } + + // The leaf first parent is the IniKey, the second is the IniProperty (or a + // wrapping CompositeElement in some cases) + PsiElement parentPsi = leaf.getParent().getParent(); + if (parentPsi instanceof final CompositeElement composite) { + parentPsi = composite.getPsi(); + } + + if (parentPsi instanceof final IniProperty kv) { + // Get the section name, if any + String sectionName = null; + if (kv.getParent() instanceof final IniSection section) { + sectionName = section.getSectionName(); + } + + final String keyName = kv.getKeyName(); + + if (IgIniSpecs.IG_SECTION_NAME.equals(sectionName)) { + if (IgIniSpecs.TEMPLATE_KEY_NAME.equals(keyName)) { + // List from https://build.fhir.org/ig/FHIR/ig-guidance/ + resultSet.addElement(LookupElementBuilder.create("fhir.base.template")); + resultSet.addElement(LookupElementBuilder.create("hl7.base.template")); + resultSet.addElement(LookupElementBuilder.create("hl7.fhir.template")); + resultSet.addElement(LookupElementBuilder.create("hl7.davinci.template")); + resultSet.addElement(LookupElementBuilder.create("hl7.cda.template")); + resultSet.addElement(LookupElementBuilder.create("hl7.other.template")); + } + } + } + } + } + } + ); + + // Provide completion for section names + this.extend(CompletionType.BASIC, + PlatformPatterns.psiElement(IniTypes.SECTION_NAME), + new CompletionProvider<>() { + public void addCompletions(@NotNull CompletionParameters parameters, + @NotNull ProcessingContext context, + @NotNull CompletionResultSet resultSet) { + resultSet.addElement(LookupElementBuilder.create(IgIniSpecs.IG_SECTION_NAME)); + } + } + ); + } +} diff --git a/src/main/java/ch/qligier/jetbrains/plugin/fss/igini/IgIniFileType.java b/src/main/java/ch/qligier/jetbrains/plugin/fss/igini/IgIniFileType.java new file mode 100644 index 0000000..d03ad63 --- /dev/null +++ b/src/main/java/ch/qligier/jetbrains/plugin/fss/igini/IgIniFileType.java @@ -0,0 +1,60 @@ +// Copyright 2024 Quentin Ligier. Use of this source code is governed by the MIT license. + +package ch.qligier.jetbrains.plugin.fss.igini; + +import ch.qligier.jetbrains.plugin.fss.common.Icons; +import com.intellij.openapi.fileTypes.LanguageFileType; +import com.intellij.openapi.util.NlsContexts; +import com.intellij.openapi.util.NlsSafe; +import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; + +/** + * The file type for the ig.ini file. + * + * @author Quentin Ligier + **/ +public class IgIniFileType extends LanguageFileType { + + public static final IgIniFileType INSTANCE = new IgIniFileType(); + + private IgIniFileType() { + super(IgIniLanguage.INSTANCE); + } + + /** + * Returns the name of the file type. The name must be unique among all file types registered in the system. + */ + @Override + public @NonNls @NotNull String getName() { + return "ig.ini"; + } + + /** + * Returns the user-readable description of the file type. + */ + @Override + public @NlsContexts.Label @NotNull String getDescription() { + return "ig.ini"; + } + + /** + * Returns the default extension for files of the type, not including the leading '.'. + */ + @Override + public @NlsSafe @NotNull String getDefaultExtension() { + return ".ini"; + } + + /** + * Returns the icon used for showing files of the type, or {@code null} if no icon should be shown. + */ + @Override + public @Nullable Icon getIcon() { + return Icons.FHIR_FLAME; + } +} + diff --git a/src/main/java/ch/qligier/jetbrains/plugin/fss/igini/IgIniLanguage.java b/src/main/java/ch/qligier/jetbrains/plugin/fss/igini/IgIniLanguage.java new file mode 100644 index 0000000..2d683bc --- /dev/null +++ b/src/main/java/ch/qligier/jetbrains/plugin/fss/igini/IgIniLanguage.java @@ -0,0 +1,19 @@ +// Copyright 2024 Quentin Ligier. Use of this source code is governed by the MIT license. + +package ch.qligier.jetbrains.plugin.fss.igini; + +import com.intellij.lang.Language; + +/** + * jetbrains-plugin-fss + * + * @author Quentin Ligier + **/ +public class IgIniLanguage extends Language { + + public static final IgIniLanguage INSTANCE = new IgIniLanguage(); + + private IgIniLanguage() { + super("IG_INI"); + } +} diff --git a/src/main/java/ch/qligier/jetbrains/plugin/fss/igini/IgIniSpecs.java b/src/main/java/ch/qligier/jetbrains/plugin/fss/igini/IgIniSpecs.java new file mode 100644 index 0000000..f7f25aa --- /dev/null +++ b/src/main/java/ch/qligier/jetbrains/plugin/fss/igini/IgIniSpecs.java @@ -0,0 +1,21 @@ +// Copyright 2024 Quentin Ligier. Use of this source code is governed by the MIT license. + +package ch.qligier.jetbrains.plugin.fss.igini; + +/** + * Specifications of the ig.ini file. + * + * @author Quentin Ligier + **/ +public class IgIniSpecs { + + public static final String IG_SECTION_NAME = "[IG]"; + public static final String IG_KEY_NAME = "ig"; + public static final String TEMPLATE_KEY_NAME = "template"; + + /** + * This class is not instantiable. + */ + private IgIniSpecs() { + } +} diff --git a/src/main/java/ch/qligier/jetbrains/plugin/fss/igini/language/IniCommenter.java b/src/main/java/ch/qligier/jetbrains/plugin/fss/igini/language/IniCommenter.java new file mode 100644 index 0000000..bdbf702 --- /dev/null +++ b/src/main/java/ch/qligier/jetbrains/plugin/fss/igini/language/IniCommenter.java @@ -0,0 +1,51 @@ +// Copyright 2024 Quentin Ligier. Use of this source code is governed by the MIT license. + +package ch.qligier.jetbrains.plugin.fss.igini.language; + +import com.intellij.lang.Commenter; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; + +/** + * Defines the support for "Comment with Line Comment" and "Comment with Block Comment" actions in the INI language. + * + * @author Quentin Ligier + * @see Commenter + **/ +public class IniCommenter implements Commenter { + + public static final String LINE_COMMENT_PREFIX = ";"; + public static final String ALT_LINE_COMMENT_PREFIX = "#"; + + @Override + public @Nullable String getLineCommentPrefix() { + return LINE_COMMENT_PREFIX; + } + + @Override + public @NotNull List getLineCommentPrefixes() { + return List.of(LINE_COMMENT_PREFIX, ALT_LINE_COMMENT_PREFIX); + } + + @Override + public @Nullable String getBlockCommentPrefix() { + return null; + } + + @Override + public @Nullable String getBlockCommentSuffix() { + return null; + } + + @Override + public @Nullable String getCommentedBlockCommentPrefix() { + return null; + } + + @Override + public @Nullable String getCommentedBlockCommentSuffix() { + return null; + } +} diff --git a/src/main/java/ch/qligier/jetbrains/plugin/fss/igini/language/highlighter/IniSyntaxHighlighter.java b/src/main/java/ch/qligier/jetbrains/plugin/fss/igini/language/highlighter/IniSyntaxHighlighter.java new file mode 100644 index 0000000..f7afae0 --- /dev/null +++ b/src/main/java/ch/qligier/jetbrains/plugin/fss/igini/language/highlighter/IniSyntaxHighlighter.java @@ -0,0 +1,78 @@ +// Copyright 2024 Quentin Ligier. Use of this source code is governed by the MIT license. + +package ch.qligier.jetbrains.plugin.fss.igini.language.highlighter; + +import ch.qligier.jetbrains.plugin.fss.igini.language.lexer.IniLexerAdapter; +import ch.qligier.jetbrains.plugin.fss.igini.language.psi.IniTypes; +import com.intellij.lexer.Lexer; +import com.intellij.openapi.editor.DefaultLanguageHighlighterColors; +import com.intellij.openapi.editor.HighlighterColors; +import com.intellij.openapi.editor.colors.TextAttributesKey; +import com.intellij.openapi.fileTypes.SyntaxHighlighterBase; +import com.intellij.psi.TokenType; +import com.intellij.psi.tree.IElementType; +import org.jetbrains.annotations.NotNull; + +import static com.intellij.openapi.editor.colors.TextAttributesKey.createTextAttributesKey; + +/** + * The syntax highlighter for the INI file format. + * + * @author Quentin Ligier + * @see Syntax + * Highlighter and Color Settings Page + * @see Syntax and + * Error Highlighting + **/ +public class IniSyntaxHighlighter extends SyntaxHighlighterBase { + + public static final TextAttributesKey SEPARATOR = + createTextAttributesKey("INI_SEPARATOR", DefaultLanguageHighlighterColors.OPERATION_SIGN); + public static final TextAttributesKey SECTION = + createTextAttributesKey("INI_SECTION", DefaultLanguageHighlighterColors.KEYWORD); + public static final TextAttributesKey KEY = + createTextAttributesKey("INI_KEY", DefaultLanguageHighlighterColors.MARKUP_ATTRIBUTE); + public static final TextAttributesKey VALUE = + createTextAttributesKey("INI_VALUE", DefaultLanguageHighlighterColors.STRING); + public static final TextAttributesKey COMMENT = + createTextAttributesKey("INI_COMMENT", DefaultLanguageHighlighterColors.LINE_COMMENT); + public static final TextAttributesKey BAD_CHARACTER = + createTextAttributesKey("INI_BAD_CHARACTER", HighlighterColors.BAD_CHARACTER); + + private static final TextAttributesKey[] BAD_CHAR_KEYS = new TextAttributesKey[]{BAD_CHARACTER}; + private static final TextAttributesKey[] SEPARATOR_KEYS = new TextAttributesKey[]{SEPARATOR}; + private static final TextAttributesKey[] SECTION_KEYS = new TextAttributesKey[]{SECTION}; + private static final TextAttributesKey[] KEY_KEYS = new TextAttributesKey[]{KEY}; + private static final TextAttributesKey[] VALUE_KEYS = new TextAttributesKey[]{VALUE}; + private static final TextAttributesKey[] COMMENT_KEYS = new TextAttributesKey[]{COMMENT}; + private static final TextAttributesKey[] EMPTY_KEYS = new TextAttributesKey[0]; + + + @Override + public @NotNull Lexer getHighlightingLexer() { + return new IniLexerAdapter(); + } + + @Override + public TextAttributesKey @NotNull [] getTokenHighlights(final IElementType iElementType) { + if (iElementType.equals(IniTypes.EQUAL)) { + return SEPARATOR_KEYS; + } + if (iElementType.equals(IniTypes.SECTION_NAME)) { + return SECTION_KEYS; + } + if (iElementType.equals(IniTypes.PKEY)) { + return KEY_KEYS; + } + if (iElementType.equals(IniTypes.PVALUE)) { + return VALUE_KEYS; + } + if (iElementType.equals(IniTypes.COMMENT)) { + return COMMENT_KEYS; + } + if (iElementType.equals(TokenType.BAD_CHARACTER)) { + return BAD_CHAR_KEYS; + } + return EMPTY_KEYS; + } +} diff --git a/src/main/java/ch/qligier/jetbrains/plugin/fss/igini/language/highlighter/IniSyntaxHighlighterFactory.java b/src/main/java/ch/qligier/jetbrains/plugin/fss/igini/language/highlighter/IniSyntaxHighlighterFactory.java new file mode 100644 index 0000000..8af5f15 --- /dev/null +++ b/src/main/java/ch/qligier/jetbrains/plugin/fss/igini/language/highlighter/IniSyntaxHighlighterFactory.java @@ -0,0 +1,28 @@ +// Copyright 2024 Quentin Ligier. Use of this source code is governed by the MIT license. + +package ch.qligier.jetbrains.plugin.fss.igini.language.highlighter; + +import com.intellij.openapi.fileTypes.SyntaxHighlighter; +import com.intellij.openapi.fileTypes.SyntaxHighlighterFactory; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.vfs.VirtualFile; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * The factory of the INI syntax highlighter. + * + * @author Quentin Ligier + * @see Syntax + * Highlighter and Color Settings Page + * @see Syntax and + * Error Highlighting + **/ +public class IniSyntaxHighlighterFactory extends SyntaxHighlighterFactory { + + @Override + public @NotNull SyntaxHighlighter getSyntaxHighlighter(@Nullable final Project project, + @Nullable final VirtualFile virtualFile) { + return new IniSyntaxHighlighter(); + } +} diff --git a/src/main/java/ch/qligier/jetbrains/plugin/fss/igini/language/lexer/IniLexerAdapter.java b/src/main/java/ch/qligier/jetbrains/plugin/fss/igini/language/lexer/IniLexerAdapter.java new file mode 100644 index 0000000..2baf630 --- /dev/null +++ b/src/main/java/ch/qligier/jetbrains/plugin/fss/igini/language/lexer/IniLexerAdapter.java @@ -0,0 +1,18 @@ +// Copyright 2024 Quentin Ligier. Use of this source code is governed by the MIT license. + +package ch.qligier.jetbrains.plugin.fss.igini.language.lexer; + +import ch.qligier.jetbrains.plugin.fss.igini.language.IniLexer; +import com.intellij.lexer.FlexAdapter; + +/** + * jetbrains-plugin-fss + * + * @author Quentin Ligier + **/ +public class IniLexerAdapter extends FlexAdapter { + + public IniLexerAdapter() { + super(new IniLexer(null)); + } +} diff --git a/src/main/java/ch/qligier/jetbrains/plugin/fss/igini/language/lexer/ini.flex b/src/main/java/ch/qligier/jetbrains/plugin/fss/igini/language/lexer/ini.flex new file mode 100644 index 0000000..e283ce9 --- /dev/null +++ b/src/main/java/ch/qligier/jetbrains/plugin/fss/igini/language/lexer/ini.flex @@ -0,0 +1,55 @@ +// Copyright 2024 Quentin Ligier. Use of this source code is governed by the MIT license. +// A lexer for the INI file format, used to parse the ig.ini file. +// See https://plugins.jetbrains.com/docs/intellij/lexer-and-parser-definition.html +// See https://plugins.jetbrains.com/docs/intellij/implementing-lexer.html +package ch.qligier.jetbrains.plugin.fss.igini.language; + +import com.intellij.lexer.FlexLexer; +import com.intellij.psi.tree.IElementType; +import ch.qligier.jetbrains.plugin.fss.igini.language.psi.IniTypes; +import com.intellij.psi.TokenType; + +%% + +%class IniLexer +%implements FlexLexer +%unicode +%public +%function advance +%type IElementType +%eof{ return; +%eof} + +EOL= \n | \r | \r\n +COMMENT=[;#][^\r\n]* + +PVALUE=[^\n\r\f\t\=\ ][^\n\r\f\t\=]* +PKEY=[^\n\r\f\ \t\=]+ + +WHITESPACE=[\ \t\f]+ +SECTION_NAME=\[[^\]]+\] +EQUAL=\= + +%state IN_VALUE +%state IN_KEY_VALUE_SEPARATOR + +%% + +{COMMENT} { return IniTypes.COMMENT; } +{WHITESPACE} { return TokenType.WHITE_SPACE; } +{SECTION_NAME} { return IniTypes.SECTION_NAME; } + + { + {PKEY} { yybegin(IN_KEY_VALUE_SEPARATOR); return IniTypes.PKEY; } + {EOL}+ { return TokenType.WHITE_SPACE; } +} + { + {EQUAL} { yybegin(IN_VALUE); return IniTypes.EQUAL; } + {EOL} { yybegin(YYINITIAL); return TokenType.WHITE_SPACE; } +} + { + {PVALUE} { return IniTypes.PVALUE; } + {EOL} { yybegin(YYINITIAL); return TokenType.WHITE_SPACE; } +} + +. { return TokenType.BAD_CHARACTER; } diff --git a/src/main/java/ch/qligier/jetbrains/plugin/fss/igini/language/parser/IniParser.java b/src/main/java/ch/qligier/jetbrains/plugin/fss/igini/language/parser/IniParser.java new file mode 100644 index 0000000..733e5ef --- /dev/null +++ b/src/main/java/ch/qligier/jetbrains/plugin/fss/igini/language/parser/IniParser.java @@ -0,0 +1,153 @@ +// This is a generated file. Not intended for manual editing. +package ch.qligier.jetbrains.plugin.fss.igini.language.parser; + +import com.intellij.lang.PsiBuilder; +import com.intellij.lang.PsiBuilder.Marker; +import static ch.qligier.jetbrains.plugin.fss.igini.language.psi.IniTypes.*; +import static ch.qligier.jetbrains.plugin.fss.igini.language.parser.IniParserUtil.*; +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 IniParser 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 iniFile(b, l + 1); + } + + /* ********************************************************** */ + // (property|COMMENT)* (section|property|COMMENT)* + static boolean iniFile(PsiBuilder b, int l) { + if (!recursion_guard_(b, l, "iniFile")) return false; + boolean r; + Marker m = enter_section_(b); + r = iniFile_0(b, l + 1); + r = r && iniFile_1(b, l + 1); + exit_section_(b, m, null, r); + return r; + } + + // (property|COMMENT)* + private static boolean iniFile_0(PsiBuilder b, int l) { + if (!recursion_guard_(b, l, "iniFile_0")) return false; + while (true) { + int c = current_position_(b); + if (!iniFile_0_0(b, l + 1)) break; + if (!empty_element_parsed_guard_(b, "iniFile_0", c)) break; + } + return true; + } + + // property|COMMENT + private static boolean iniFile_0_0(PsiBuilder b, int l) { + if (!recursion_guard_(b, l, "iniFile_0_0")) return false; + boolean r; + r = property(b, l + 1); + if (!r) r = consumeToken(b, COMMENT); + return r; + } + + // (section|property|COMMENT)* + private static boolean iniFile_1(PsiBuilder b, int l) { + if (!recursion_guard_(b, l, "iniFile_1")) return false; + while (true) { + int c = current_position_(b); + if (!iniFile_1_0(b, l + 1)) break; + if (!empty_element_parsed_guard_(b, "iniFile_1", c)) break; + } + return true; + } + + // section|property|COMMENT + private static boolean iniFile_1_0(PsiBuilder b, int l) { + if (!recursion_guard_(b, l, "iniFile_1_0")) return false; + boolean r; + r = section(b, l + 1); + if (!r) r = property(b, l + 1); + if (!r) r = consumeToken(b, COMMENT); + return r; + } + + /* ********************************************************** */ + // PKEY + public static boolean key(PsiBuilder b, int l) { + if (!recursion_guard_(b, l, "key")) return false; + if (!nextTokenIs(b, PKEY)) return false; + boolean r; + Marker m = enter_section_(b); + r = consumeToken(b, PKEY); + exit_section_(b, m, KEY, r); + return r; + } + + /* ********************************************************** */ + // key EQUAL value + public static boolean property(PsiBuilder b, int l) { + if (!recursion_guard_(b, l, "property")) return false; + if (!nextTokenIs(b, PKEY)) return false; + boolean r; + Marker m = enter_section_(b); + r = key(b, l + 1); + r = r && consumeToken(b, EQUAL); + r = r && value(b, l + 1); + exit_section_(b, m, PROPERTY, r); + return r; + } + + /* ********************************************************** */ + // SECTION_NAME property* + public static boolean section(PsiBuilder b, int l) { + if (!recursion_guard_(b, l, "section")) return false; + if (!nextTokenIs(b, SECTION_NAME)) return false; + boolean r; + Marker m = enter_section_(b); + r = consumeToken(b, SECTION_NAME); + r = r && section_1(b, l + 1); + exit_section_(b, m, SECTION, r); + return r; + } + + // property* + private static boolean section_1(PsiBuilder b, int l) { + if (!recursion_guard_(b, l, "section_1")) return false; + while (true) { + int c = current_position_(b); + if (!property(b, l + 1)) break; + if (!empty_element_parsed_guard_(b, "section_1", c)) break; + } + return true; + } + + /* ********************************************************** */ + // PVALUE + public static boolean value(PsiBuilder b, int l) { + if (!recursion_guard_(b, l, "value")) return false; + if (!nextTokenIs(b, PVALUE)) return false; + boolean r; + Marker m = enter_section_(b); + r = consumeToken(b, PVALUE); + exit_section_(b, m, VALUE, r); + return r; + } + +} diff --git a/src/main/java/ch/qligier/jetbrains/plugin/fss/igini/language/parser/IniParserDefinition.java b/src/main/java/ch/qligier/jetbrains/plugin/fss/igini/language/parser/IniParserDefinition.java new file mode 100644 index 0000000..b6f96e8 --- /dev/null +++ b/src/main/java/ch/qligier/jetbrains/plugin/fss/igini/language/parser/IniParserDefinition.java @@ -0,0 +1,74 @@ +// Copyright 2024 Quentin Ligier. Use of this source code is governed by the MIT license. + +package ch.qligier.jetbrains.plugin.fss.igini.language.parser; + +import ch.qligier.jetbrains.plugin.fss.igini.IgIniLanguage; +import ch.qligier.jetbrains.plugin.fss.igini.language.lexer.IniLexerAdapter; +import ch.qligier.jetbrains.plugin.fss.igini.language.psi.IniFile; +import ch.qligier.jetbrains.plugin.fss.igini.language.psi.IniTokenSets; +import ch.qligier.jetbrains.plugin.fss.igini.language.psi.IniTypes; +import com.intellij.lang.ASTNode; +import com.intellij.lang.ParserDefinition; +import com.intellij.lang.PsiParser; +import com.intellij.lexer.Lexer; +import com.intellij.openapi.project.Project; +import com.intellij.psi.FileViewProvider; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiFile; +import com.intellij.psi.tree.IFileElementType; +import com.intellij.psi.tree.TokenSet; +import org.jetbrains.annotations.NotNull; + +/** + * Defines the implementation of a parser for the INI language. + * + * @author Quentin Ligier + * @see Define a + * Parser + **/ +public class IniParserDefinition implements ParserDefinition { + + public static final IFileElementType FILE = new IFileElementType(IgIniLanguage.INSTANCE); + + @NotNull + @Override + public Lexer createLexer(Project project) { + return new IniLexerAdapter(); + } + + @NotNull + @Override + public TokenSet getCommentTokens() { + return IniTokenSets.COMMENTS; + } + + @NotNull + @Override + public TokenSet getStringLiteralElements() { + return TokenSet.EMPTY; + } + + @NotNull + @Override + public PsiParser createParser(final Project project) { + return new IniParser(); + } + + @NotNull + @Override + public IFileElementType getFileNodeType() { + return FILE; + } + + @NotNull + @Override + public PsiFile createFile(@NotNull final FileViewProvider viewProvider) { + return new IniFile(viewProvider); + } + + @NotNull + @Override + public PsiElement createElement(final ASTNode node) { + return IniTypes.Factory.createElement(node); + } +} diff --git a/src/main/java/ch/qligier/jetbrains/plugin/fss/igini/language/parser/IniParserUtil.java b/src/main/java/ch/qligier/jetbrains/plugin/fss/igini/language/parser/IniParserUtil.java new file mode 100644 index 0000000..f2e10ac --- /dev/null +++ b/src/main/java/ch/qligier/jetbrains/plugin/fss/igini/language/parser/IniParserUtil.java @@ -0,0 +1,13 @@ +// Copyright 2024 Quentin Ligier. Use of this source code is governed by the MIT license. + +package ch.qligier.jetbrains.plugin.fss.igini.language.parser; + +import com.intellij.lang.parser.GeneratedParserUtilBase; + +/** + * Some utilities used by the generated parser. + * + * @author Quentin Ligier + **/ +public class IniParserUtil extends GeneratedParserUtilBase { +} diff --git a/src/main/java/ch/qligier/jetbrains/plugin/fss/igini/language/parser/ini.bnf b/src/main/java/ch/qligier/jetbrains/plugin/fss/igini/language/parser/ini.bnf new file mode 100644 index 0000000..6e23ce0 --- /dev/null +++ b/src/main/java/ch/qligier/jetbrains/plugin/fss/igini/language/parser/ini.bnf @@ -0,0 +1,33 @@ +/* + * Copyright 2024 Quentin Ligier. Use of this source code is governed by the MIT license. + */ + +/* + * A parser for the INI file format, used to parse the ig.ini file. + * See https://plugins.jetbrains.com/docs/intellij/grammar-and-parser.html + * See https://plugins.jetbrains.com/docs/intellij/implementing-parser-and-psi.html +*/ + +{ + parserClass="ch.qligier.jetbrains.plugin.fss.igini.language.parser.IniParser" + parserUtilClass="ch.qligier.jetbrains.plugin.fss.igini.language.parser.IniParserUtil" + + extends="com.intellij.extapi.psi.ASTWrapperPsiElement" + + psiClassPrefix="Ini" + psiImplClassSuffix="Impl" + psiPackage="ch.qligier.jetbrains.plugin.fss.igini.language.psi" + psiImplPackage="ch.qligier.jetbrains.plugin.fss.igini.language.psi.impl" + + elementTypeHolderClass="ch.qligier.jetbrains.plugin.fss.igini.language.psi.IniTypes" + elementTypeClass="ch.qligier.jetbrains.plugin.fss.igini.language.psi.IniElementType" + tokenTypeClass="ch.qligier.jetbrains.plugin.fss.igini.language.psi.IniTokenType" + + psiImplUtilClass="ch.qligier.jetbrains.plugin.fss.igini.language.psi.impl.IniPsiImplUtil" +} + +iniFile ::= (property|COMMENT)* (section|property|COMMENT)* +section ::= SECTION_NAME property* { methods = [getSectionName getProperties] } +property ::= key EQUAL value { methods = [getKeyName getKeyElement] } +key ::= PKEY +value ::= PVALUE diff --git a/src/main/java/ch/qligier/jetbrains/plugin/fss/igini/language/psi/IniElementType.java b/src/main/java/ch/qligier/jetbrains/plugin/fss/igini/language/psi/IniElementType.java new file mode 100644 index 0000000..6c7ed06 --- /dev/null +++ b/src/main/java/ch/qligier/jetbrains/plugin/fss/igini/language/psi/IniElementType.java @@ -0,0 +1,20 @@ +// Copyright 2024 Quentin Ligier. Use of this source code is governed by the MIT license. + +package ch.qligier.jetbrains.plugin.fss.igini.language.psi; + +import ch.qligier.jetbrains.plugin.fss.igini.IgIniLanguage; +import com.intellij.psi.tree.IElementType; +import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; + +/** + * jetbrains-plugin-fss + * + * @author Quentin Ligier + **/ +public class IniElementType extends IElementType { + + public IniElementType(@NonNls @NotNull final String debugName) { + super(debugName, IgIniLanguage.INSTANCE); + } +} diff --git a/src/main/java/ch/qligier/jetbrains/plugin/fss/igini/language/psi/IniFile.java b/src/main/java/ch/qligier/jetbrains/plugin/fss/igini/language/psi/IniFile.java new file mode 100644 index 0000000..5fbdcd3 --- /dev/null +++ b/src/main/java/ch/qligier/jetbrains/plugin/fss/igini/language/psi/IniFile.java @@ -0,0 +1,43 @@ +// Copyright 2024 Quentin Ligier. Use of this source code is governed by the MIT license. + +package ch.qligier.jetbrains.plugin.fss.igini.language.psi; + +import ch.qligier.jetbrains.plugin.fss.igini.IgIniFileType; +import ch.qligier.jetbrains.plugin.fss.igini.IgIniLanguage; +import ch.qligier.jetbrains.plugin.fss.igini.language.psi.impl.IniPsiImplUtil; +import com.intellij.extapi.psi.PsiFileBase; +import com.intellij.openapi.fileTypes.FileType; +import com.intellij.psi.FileViewProvider; +import org.jetbrains.annotations.NotNull; + +import java.util.Collection; + +/** + * jetbrains-plugin-fss + * + * @author Quentin Ligier + **/ +public class IniFile extends PsiFileBase { + + public IniFile(@NotNull final FileViewProvider viewProvider) { + super(viewProvider, IgIniLanguage.INSTANCE); + } + + @Override + public @NotNull FileType getFileType() { + return IgIniFileType.INSTANCE; + } + + @Override + public String toString() { + return "ig.ini file"; + } + + public @NotNull Collection getSections() { + return IniPsiImplUtil.getSections(this); + } + + public @NotNull Collection getProperties() { + return IniPsiImplUtil.getProperties(this); + } +} diff --git a/src/main/java/ch/qligier/jetbrains/plugin/fss/igini/language/psi/IniKey.java b/src/main/java/ch/qligier/jetbrains/plugin/fss/igini/language/psi/IniKey.java new file mode 100644 index 0000000..0587ccc --- /dev/null +++ b/src/main/java/ch/qligier/jetbrains/plugin/fss/igini/language/psi/IniKey.java @@ -0,0 +1,10 @@ +// This is a generated file. Not intended for manual editing. +package ch.qligier.jetbrains.plugin.fss.igini.language.psi; + +import java.util.List; +import org.jetbrains.annotations.*; +import com.intellij.psi.PsiElement; + +public interface IniKey extends PsiElement { + +} diff --git a/src/main/java/ch/qligier/jetbrains/plugin/fss/igini/language/psi/IniProperty.java b/src/main/java/ch/qligier/jetbrains/plugin/fss/igini/language/psi/IniProperty.java new file mode 100644 index 0000000..0054340 --- /dev/null +++ b/src/main/java/ch/qligier/jetbrains/plugin/fss/igini/language/psi/IniProperty.java @@ -0,0 +1,20 @@ +// This is a generated file. Not intended for manual editing. +package ch.qligier.jetbrains.plugin.fss.igini.language.psi; + +import java.util.List; +import org.jetbrains.annotations.*; +import com.intellij.psi.PsiElement; + +public interface IniProperty extends PsiElement { + + @NotNull + IniKey getKey(); + + @NotNull + IniValue getValue(); + + @Nullable String getKeyName(); + + @Nullable IniKey getKeyElement(); + +} diff --git a/src/main/java/ch/qligier/jetbrains/plugin/fss/igini/language/psi/IniSection.java b/src/main/java/ch/qligier/jetbrains/plugin/fss/igini/language/psi/IniSection.java new file mode 100644 index 0000000..a21cc99 --- /dev/null +++ b/src/main/java/ch/qligier/jetbrains/plugin/fss/igini/language/psi/IniSection.java @@ -0,0 +1,17 @@ +// This is a generated file. Not intended for manual editing. +package ch.qligier.jetbrains.plugin.fss.igini.language.psi; + +import java.util.List; +import org.jetbrains.annotations.*; +import com.intellij.psi.PsiElement; + +public interface IniSection extends PsiElement { + + @NotNull + List getPropertyList(); + + @Nullable String getSectionName(); + + @NotNull List getProperties(); + +} diff --git a/src/main/java/ch/qligier/jetbrains/plugin/fss/igini/language/psi/IniTokenSets.java b/src/main/java/ch/qligier/jetbrains/plugin/fss/igini/language/psi/IniTokenSets.java new file mode 100644 index 0000000..a6d42a0 --- /dev/null +++ b/src/main/java/ch/qligier/jetbrains/plugin/fss/igini/language/psi/IniTokenSets.java @@ -0,0 +1,15 @@ +// Copyright 2024 Quentin Ligier. Use of this source code is governed by the MIT license. + +package ch.qligier.jetbrains.plugin.fss.igini.language.psi; + +import com.intellij.psi.tree.TokenSet; + +/** + * jetbrains-plugin-fss + * + * @author Quentin Ligier + **/ +public interface IniTokenSets { + + TokenSet COMMENTS = TokenSet.create(IniTypes.COMMENT); +} diff --git a/src/main/java/ch/qligier/jetbrains/plugin/fss/igini/language/psi/IniTokenType.java b/src/main/java/ch/qligier/jetbrains/plugin/fss/igini/language/psi/IniTokenType.java new file mode 100644 index 0000000..19fcd7b --- /dev/null +++ b/src/main/java/ch/qligier/jetbrains/plugin/fss/igini/language/psi/IniTokenType.java @@ -0,0 +1,25 @@ +// Copyright 2024 Quentin Ligier. Use of this source code is governed by the MIT license. + +package ch.qligier.jetbrains.plugin.fss.igini.language.psi; + +import ch.qligier.jetbrains.plugin.fss.igini.IgIniLanguage; +import com.intellij.psi.tree.IElementType; +import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; + +/** + * jetbrains-plugin-fss + * + * @author Quentin Ligier + **/ +public class IniTokenType extends IElementType { + + public IniTokenType(@NonNls @NotNull final String debugName) { + super(debugName, IgIniLanguage.INSTANCE); + } + + @Override + public String toString() { + return "IniTokenType." + super.toString(); + } +} diff --git a/src/main/java/ch/qligier/jetbrains/plugin/fss/igini/language/psi/IniTypes.java b/src/main/java/ch/qligier/jetbrains/plugin/fss/igini/language/psi/IniTypes.java new file mode 100644 index 0000000..4a5fef3 --- /dev/null +++ b/src/main/java/ch/qligier/jetbrains/plugin/fss/igini/language/psi/IniTypes.java @@ -0,0 +1,40 @@ +// This is a generated file. Not intended for manual editing. +package ch.qligier.jetbrains.plugin.fss.igini.language.psi; + +import com.intellij.psi.tree.IElementType; +import com.intellij.psi.PsiElement; +import com.intellij.lang.ASTNode; +import ch.qligier.jetbrains.plugin.fss.igini.language.psi.impl.*; + +public interface IniTypes { + + IElementType KEY = new IniElementType("KEY"); + IElementType PROPERTY = new IniElementType("PROPERTY"); + IElementType SECTION = new IniElementType("SECTION"); + IElementType VALUE = new IniElementType("VALUE"); + + IElementType COMMENT = new IniTokenType("COMMENT"); + IElementType EQUAL = new IniTokenType("EQUAL"); + IElementType PKEY = new IniTokenType("PKEY"); + IElementType PVALUE = new IniTokenType("PVALUE"); + IElementType SECTION_NAME = new IniTokenType("SECTION_NAME"); + + class Factory { + public static PsiElement createElement(ASTNode node) { + IElementType type = node.getElementType(); + if (type == KEY) { + return new IniKeyImpl(node); + } + else if (type == PROPERTY) { + return new IniPropertyImpl(node); + } + else if (type == SECTION) { + return new IniSectionImpl(node); + } + else if (type == VALUE) { + return new IniValueImpl(node); + } + throw new AssertionError("Unknown element type: " + type); + } + } +} diff --git a/src/main/java/ch/qligier/jetbrains/plugin/fss/igini/language/psi/IniValue.java b/src/main/java/ch/qligier/jetbrains/plugin/fss/igini/language/psi/IniValue.java new file mode 100644 index 0000000..91bb215 --- /dev/null +++ b/src/main/java/ch/qligier/jetbrains/plugin/fss/igini/language/psi/IniValue.java @@ -0,0 +1,10 @@ +// This is a generated file. Not intended for manual editing. +package ch.qligier.jetbrains.plugin.fss.igini.language.psi; + +import java.util.List; +import org.jetbrains.annotations.*; +import com.intellij.psi.PsiElement; + +public interface IniValue extends PsiElement { + +} diff --git a/src/main/java/ch/qligier/jetbrains/plugin/fss/igini/language/psi/IniVisitor.java b/src/main/java/ch/qligier/jetbrains/plugin/fss/igini/language/psi/IniVisitor.java new file mode 100644 index 0000000..eda4e65 --- /dev/null +++ b/src/main/java/ch/qligier/jetbrains/plugin/fss/igini/language/psi/IniVisitor.java @@ -0,0 +1,30 @@ +// This is a generated file. Not intended for manual editing. +package ch.qligier.jetbrains.plugin.fss.igini.language.psi; + +import org.jetbrains.annotations.*; +import com.intellij.psi.PsiElementVisitor; +import com.intellij.psi.PsiElement; + +public class IniVisitor extends PsiElementVisitor { + + public void visitKey(@NotNull IniKey o) { + visitPsiElement(o); + } + + public void visitProperty(@NotNull IniProperty o) { + visitPsiElement(o); + } + + public void visitSection(@NotNull IniSection o) { + visitPsiElement(o); + } + + public void visitValue(@NotNull IniValue o) { + visitPsiElement(o); + } + + public void visitPsiElement(@NotNull PsiElement o) { + visitElement(o); + } + +} diff --git a/src/main/java/ch/qligier/jetbrains/plugin/fss/igini/language/psi/impl/IniKeyImpl.java b/src/main/java/ch/qligier/jetbrains/plugin/fss/igini/language/psi/impl/IniKeyImpl.java new file mode 100644 index 0000000..c997ce0 --- /dev/null +++ b/src/main/java/ch/qligier/jetbrains/plugin/fss/igini/language/psi/impl/IniKeyImpl.java @@ -0,0 +1,30 @@ +// This is a generated file. Not intended for manual editing. +package ch.qligier.jetbrains.plugin.fss.igini.language.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 ch.qligier.jetbrains.plugin.fss.igini.language.psi.IniTypes.*; +import com.intellij.extapi.psi.ASTWrapperPsiElement; +import ch.qligier.jetbrains.plugin.fss.igini.language.psi.*; + +public class IniKeyImpl extends ASTWrapperPsiElement implements IniKey { + + public IniKeyImpl(@NotNull ASTNode node) { + super(node); + } + + public void accept(@NotNull IniVisitor visitor) { + visitor.visitKey(this); + } + + @Override + public void accept(@NotNull PsiElementVisitor visitor) { + if (visitor instanceof IniVisitor) accept((IniVisitor)visitor); + else super.accept(visitor); + } + +} diff --git a/src/main/java/ch/qligier/jetbrains/plugin/fss/igini/language/psi/impl/IniPropertyImpl.java b/src/main/java/ch/qligier/jetbrains/plugin/fss/igini/language/psi/impl/IniPropertyImpl.java new file mode 100644 index 0000000..2985ace --- /dev/null +++ b/src/main/java/ch/qligier/jetbrains/plugin/fss/igini/language/psi/impl/IniPropertyImpl.java @@ -0,0 +1,52 @@ +// This is a generated file. Not intended for manual editing. +package ch.qligier.jetbrains.plugin.fss.igini.language.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 ch.qligier.jetbrains.plugin.fss.igini.language.psi.IniTypes.*; +import com.intellij.extapi.psi.ASTWrapperPsiElement; +import ch.qligier.jetbrains.plugin.fss.igini.language.psi.*; + +public class IniPropertyImpl extends ASTWrapperPsiElement implements IniProperty { + + public IniPropertyImpl(@NotNull ASTNode node) { + super(node); + } + + public void accept(@NotNull IniVisitor visitor) { + visitor.visitProperty(this); + } + + @Override + public void accept(@NotNull PsiElementVisitor visitor) { + if (visitor instanceof IniVisitor) accept((IniVisitor)visitor); + else super.accept(visitor); + } + + @Override + @NotNull + public IniKey getKey() { + return findNotNullChildByClass(IniKey.class); + } + + @Override + @NotNull + public IniValue getValue() { + return findNotNullChildByClass(IniValue.class); + } + + @Override + public @Nullable String getKeyName() { + return IniPsiImplUtil.getKeyName(this); + } + + @Override + public @Nullable IniKey getKeyElement() { + return IniPsiImplUtil.getKeyElement(this); + } + +} diff --git a/src/main/java/ch/qligier/jetbrains/plugin/fss/igini/language/psi/impl/IniPsiImplUtil.java b/src/main/java/ch/qligier/jetbrains/plugin/fss/igini/language/psi/impl/IniPsiImplUtil.java new file mode 100644 index 0000000..51cb7c9 --- /dev/null +++ b/src/main/java/ch/qligier/jetbrains/plugin/fss/igini/language/psi/impl/IniPsiImplUtil.java @@ -0,0 +1,53 @@ +// Copyright 2024 Quentin Ligier. Use of this source code is governed by the MIT license. + +package ch.qligier.jetbrains.plugin.fss.igini.language.psi.impl; + +import ch.qligier.jetbrains.plugin.fss.igini.language.psi.*; +import com.intellij.lang.ASTNode; +import com.intellij.psi.util.PsiTreeUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; + +/** + * jetbrains-plugin-fss + * + * @author Quentin Ligier + **/ +public class IniPsiImplUtil { + + public static @NotNull List getSections(final IniFile file) { + return PsiTreeUtil.getChildrenOfTypeAsList(file, IniSection.class); + } + + public static @NotNull List getProperties(final IniFile file) { + return PsiTreeUtil.getChildrenOfTypeAsList(file, IniProperty.class); + } + + public static @Nullable IniKey getKeyElement(final IniProperty element) { + return PsiTreeUtil.getChildOfType(element, IniKey.class); + } + + public static @Nullable String getKeyName(final IniProperty element) { + final ASTNode keyNode = element.getNode().findChildByType(IniTypes.KEY); + if (keyNode != null) { + return keyNode.getText(); + } else { + return null; + } + } + + public static @Nullable String getSectionName(final IniSection element) { + final ASTNode keyNode = element.getNode().findChildByType(IniTypes.SECTION_NAME); + if (keyNode != null) { + return keyNode.getText(); + } else { + return null; + } + } + + public static @NotNull List getProperties(final IniSection section) { + return PsiTreeUtil.getChildrenOfTypeAsList(section, IniProperty.class); + } +} diff --git a/src/main/java/ch/qligier/jetbrains/plugin/fss/igini/language/psi/impl/IniSectionImpl.java b/src/main/java/ch/qligier/jetbrains/plugin/fss/igini/language/psi/impl/IniSectionImpl.java new file mode 100644 index 0000000..15629cb --- /dev/null +++ b/src/main/java/ch/qligier/jetbrains/plugin/fss/igini/language/psi/impl/IniSectionImpl.java @@ -0,0 +1,46 @@ +// This is a generated file. Not intended for manual editing. +package ch.qligier.jetbrains.plugin.fss.igini.language.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 ch.qligier.jetbrains.plugin.fss.igini.language.psi.IniTypes.*; +import com.intellij.extapi.psi.ASTWrapperPsiElement; +import ch.qligier.jetbrains.plugin.fss.igini.language.psi.*; + +public class IniSectionImpl extends ASTWrapperPsiElement implements IniSection { + + public IniSectionImpl(@NotNull ASTNode node) { + super(node); + } + + public void accept(@NotNull IniVisitor visitor) { + visitor.visitSection(this); + } + + @Override + public void accept(@NotNull PsiElementVisitor visitor) { + if (visitor instanceof IniVisitor) accept((IniVisitor)visitor); + else super.accept(visitor); + } + + @Override + @NotNull + public List getPropertyList() { + return PsiTreeUtil.getChildrenOfTypeAsList(this, IniProperty.class); + } + + @Override + public @Nullable String getSectionName() { + return IniPsiImplUtil.getSectionName(this); + } + + @Override + public @NotNull List getProperties() { + return IniPsiImplUtil.getProperties(this); + } + +} diff --git a/src/main/java/ch/qligier/jetbrains/plugin/fss/igini/language/psi/impl/IniValueImpl.java b/src/main/java/ch/qligier/jetbrains/plugin/fss/igini/language/psi/impl/IniValueImpl.java new file mode 100644 index 0000000..a3ec169 --- /dev/null +++ b/src/main/java/ch/qligier/jetbrains/plugin/fss/igini/language/psi/impl/IniValueImpl.java @@ -0,0 +1,30 @@ +// This is a generated file. Not intended for manual editing. +package ch.qligier.jetbrains.plugin.fss.igini.language.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 ch.qligier.jetbrains.plugin.fss.igini.language.psi.IniTypes.*; +import com.intellij.extapi.psi.ASTWrapperPsiElement; +import ch.qligier.jetbrains.plugin.fss.igini.language.psi.*; + +public class IniValueImpl extends ASTWrapperPsiElement implements IniValue { + + public IniValueImpl(@NotNull ASTNode node) { + super(node); + } + + public void accept(@NotNull IniVisitor visitor) { + visitor.visitValue(this); + } + + @Override + public void accept(@NotNull PsiElementVisitor visitor) { + if (visitor instanceof IniVisitor) accept((IniVisitor)visitor); + else super.accept(visitor); + } + +} diff --git a/src/main/resources/META-INF/plugin.xml b/src/main/resources/META-INF/plugin.xml index d4b5daa..90be562 100644 --- a/src/main/resources/META-INF/plugin.xml +++ b/src/main/resources/META-INF/plugin.xml @@ -62,6 +62,30 @@ + + + + + + + + +