├── settings.gradle ├── demo.gif ├── src ├── main │ ├── resources │ │ ├── com │ │ │ └── github │ │ │ │ └── oowekyala │ │ │ │ └── ijcc │ │ │ │ ├── optionDescriptions │ │ │ │ ├── JDK_VERSION.html │ │ │ │ ├── jjtree │ │ │ │ │ ├── NODE_PACKAGE.html │ │ │ │ │ ├── BUILD_NODE_FILES.html │ │ │ │ │ ├── MULTI.html │ │ │ │ │ ├── NODE_DEFAULT_VOID.html │ │ │ │ │ ├── NODE_PREFIX.html │ │ │ │ │ ├── NODE_SCOPE_HOOK.html │ │ │ │ │ ├── NODE_CLASS.html │ │ │ │ │ ├── VISITOR.html │ │ │ │ │ ├── VISITOR_EXCEPTION.html │ │ │ │ │ ├── VISITOR_DATA_TYPE.html │ │ │ │ │ ├── VISITOR_RETURN_TYPE.html │ │ │ │ │ ├── NODE_EXTENDS.html │ │ │ │ │ ├── JJTREE_OUTPUT_DIRECTORY.html │ │ │ │ │ ├── NODE_USES_PARSER.html │ │ │ │ │ ├── TRACK_TOKENS.html │ │ │ │ │ └── NODE_FACTORY.html │ │ │ │ ├── OUTPUT_DIRECTORY.html │ │ │ │ ├── GRAMMAR_ENCODING.html │ │ │ │ ├── ERROR_REPORTING.html │ │ │ │ ├── SUPPORT_CLASS_VISIBILITY_PUBLIC.html │ │ │ │ ├── BUILD_PARSER.html │ │ │ │ ├── DEBUG_LOOKAHEAD.html │ │ │ │ ├── TOKEN_EXTENDS.html │ │ │ │ ├── UNICODE_INPUT.html │ │ │ │ ├── TOKEN_FACTORY.html │ │ │ │ ├── USER_TOKEN_MANAGER.html │ │ │ │ ├── OTHER_AMBIGUITY_CHECK.html │ │ │ │ ├── JAVA_TEMPLATE_TYPE.html │ │ │ │ ├── BUILD_TOKEN_MANAGER.html │ │ │ │ ├── IGNORE_CASE.html │ │ │ │ ├── LOOKAHEAD.html │ │ │ │ ├── TOKEN_MANAGER_USES_PARSER.html │ │ │ │ ├── JAVA_UNICODE_ESCAPE.html │ │ │ │ ├── DEBUG_PARSER.html │ │ │ │ ├── CACHE_TOKENS.html │ │ │ │ ├── DEBUG_TOKEN_MANAGER.html │ │ │ │ ├── COMMON_TOKEN_ACTION.html │ │ │ │ ├── USER_CHAR_STREAM.html │ │ │ │ ├── SANITY_CHECK.html │ │ │ │ ├── FORCE_LA_CHECK.html │ │ │ │ ├── CHOICE_AMBIGUITY_CHECK.html │ │ │ │ └── STATIC.html │ │ │ │ ├── icons │ │ │ │ ├── jccFile.svg │ │ │ │ ├── jccFile_dark.svg │ │ │ │ ├── jjtreeNode.svg │ │ │ │ ├── tree.svg │ │ │ │ ├── terminal.svg │ │ │ │ ├── jccNavigateToGrammar.svg │ │ │ │ ├── jccNavigateToGrammar_dark.svg │ │ │ │ ├── jjtreeFile.svg │ │ │ │ ├── jjtreeFile_dark.svg │ │ │ │ ├── jjtreeNodeLocate.svg │ │ │ │ ├── jjtreeNodeLocate_dark.svg │ │ │ │ ├── jjtreeNode_alt.svg │ │ │ │ ├── jccNavigateToNode.svg │ │ │ │ ├── jccNavigateToNode_dark.svg │ │ │ │ └── bnfProdAndNode.svg │ │ │ │ ├── colorSchemes │ │ │ │ ├── Darcula_Javacc.xml │ │ │ │ └── Default_Javacc.xml │ │ │ │ └── fileTemplates │ │ │ │ └── JavaCC grammar.jj.ft │ │ └── META-INF │ │ │ ├── MANIFEST.mf │ │ │ └── pluginIcon.svg │ └── kotlin │ │ └── com │ │ └── github │ │ └── oowekyala │ │ └── ijcc │ │ ├── util │ │ ├── Constants.kt │ │ ├── EnclosedLogger.kt │ │ └── SoftReferenceCache.kt │ │ ├── lang │ │ ├── lexer │ │ │ └── JavaccLexerAdapter.kt │ │ ├── model │ │ │ ├── AccessModifier.kt │ │ │ ├── RegexKind.kt │ │ │ ├── BaseCachedModelObject.kt │ │ │ ├── JccVersion.kt │ │ │ ├── J21Option.kt │ │ │ ├── GrammarNature.kt │ │ │ └── IGrammarOptions.kt │ │ ├── psi │ │ │ ├── IJccElementType.kt │ │ │ ├── IJccTokenType.kt │ │ │ ├── stubs │ │ │ │ ├── StubExtensions.kt │ │ │ │ ├── JccStubElementType.kt │ │ │ │ ├── indices │ │ │ │ │ ├── IdeStubService.kt │ │ │ │ │ ├── JjtreeQNameStubIndex.kt │ │ │ │ │ ├── JccParserQnameIndexer.kt │ │ │ │ │ └── JccStubElementTypeHolder.kt │ │ │ │ ├── DataStreamUtils.kt │ │ │ │ └── StubIndexService.kt │ │ │ ├── JccOptionBinding.kt │ │ │ ├── DepthFirstVisitor.kt │ │ │ ├── JccNonTerminalProduction.kt │ │ │ ├── JccIdentifierOwner.kt │ │ │ ├── JccNodeExtensions.kt │ │ │ ├── JccPsiElement.kt │ │ │ ├── JccScopedExpansionUnit.kt │ │ │ ├── JccJjtreeNodeDescriptorExpr.kt │ │ │ ├── impl │ │ │ │ ├── GrammarOptionsService.kt │ │ │ │ ├── JccRegexExpansionUnitImpl.kt │ │ │ │ ├── JccOptionBindingImpl.kt │ │ │ │ ├── JccRegexSpecImpl.kt │ │ │ │ ├── JccIdentifierImpl.kt │ │ │ │ ├── JjtNodeClassOwnerImpl.kt │ │ │ │ └── JccScopedExpansionUnitImpl.kt │ │ │ ├── RegexLikeDFVisitor.kt │ │ │ ├── JccTypesExt.kt │ │ │ ├── JccJavaBlock.kt │ │ │ ├── JccJavaExpression.kt │ │ │ ├── JccJavaCompilationUnit.kt │ │ │ ├── JccJavaAssignmentLhs.kt │ │ │ ├── LookaheadExtensions.kt │ │ │ ├── JccIdentifier.kt │ │ │ ├── manipulators │ │ │ │ ├── JccIdentifierManipulator.kt │ │ │ │ ├── JccLiteralRegexManipulator.kt │ │ │ │ ├── JccCommenter.kt │ │ │ │ └── JccJavaCompilationUnitManipulator.kt │ │ │ ├── PrettyPrintingExtensions.kt │ │ │ ├── JccJavaNonTerminalProductionHeader.kt │ │ │ ├── JavaccAstFactory.kt │ │ │ ├── TraversalUtil.kt │ │ │ ├── JjtNodeClassOwner.kt │ │ │ └── JccJjtreeNodeDescriptor.kt │ │ ├── injection │ │ │ ├── InjectionUtil.kt │ │ │ └── MultilineTextEscaper.kt │ │ └── cfa │ │ │ └── LastExpansion.kt │ │ ├── ide │ │ ├── refs │ │ │ ├── PsiEltResolveResult.kt │ │ │ ├── JccLexicalStateReference.kt │ │ │ ├── JccRefVariantService.kt │ │ │ ├── JccNonTerminalReference.kt │ │ │ ├── JjtNodePolyReference.kt │ │ │ └── JccBnfStringLiteralReference.kt │ │ ├── intentions │ │ │ ├── JccIntentionBase.kt │ │ │ ├── ReplaceOptionValueIntention.kt │ │ │ ├── RemoveNameFromRegexIntention.kt │ │ │ ├── DeleteExpansionIntention.kt │ │ │ ├── JccSelfTargetingEditorIntentionBase.kt │ │ │ ├── CheckRegexIntention.kt │ │ │ └── ReplaceSupersedingUsageWithReferenceIntentionFix.kt │ │ ├── highlight │ │ │ ├── JavaccSyntaxHighlighterFactory.kt │ │ │ ├── JavaccSyntaxHighlighter.kt │ │ │ └── InjectedJavaHighlightVisitor.kt │ │ ├── findusages │ │ │ ├── JccTextSelectioner.kt │ │ │ ├── JccDescriptionProvider.kt │ │ │ ├── JjtreeNodeReferenceSearcher.kt │ │ │ ├── JccUsageTypeProvider.kt │ │ │ ├── JccStringTokenFindUsagesHandlerFactory.kt │ │ │ └── StringReferenceSearcher.kt │ │ ├── completion │ │ │ ├── LookupUtil.kt │ │ │ ├── MultiCharTailType.kt │ │ │ └── JccPatterns.kt │ │ ├── gutter │ │ │ ├── BaseTargetingLineMarkerProvider.kt │ │ │ ├── JccProductionToParserLineMarkerProvider.kt │ │ │ ├── JjtreeNodeClassLineMarkerProvider.kt │ │ │ ├── JjtreePartialDeclarationLineMarkerProvider.kt │ │ │ └── JjtNodeToGrammarLineMarkerProvider.kt │ │ ├── rename │ │ │ └── JccNamesValidator.kt │ │ ├── structureview │ │ │ └── JavaccStructureViewBuilderFactory.kt │ │ ├── quickdoc │ │ │ ├── JjtNodeDocMaker.kt │ │ │ ├── JccOptionDocMaker.kt │ │ │ └── JccLexicalStateDocMaker.kt │ │ ├── inspections │ │ │ ├── JccInspectionsProvider.kt │ │ │ ├── JccInspectionBase.kt │ │ │ ├── ActionWithinLookaheadInspection.kt │ │ │ └── EmptyParserActionsInspection.kt │ │ └── folding │ │ │ └── JccFoldingOptionsProvider.kt │ │ ├── icons │ │ └── JccCoreIcons.kt │ │ ├── settings │ │ ├── JavaccProjectSettingsServiceImpl.kt │ │ ├── JavaccProjectSettingsConfigurable.kt │ │ ├── InjectionSupportLevel.kt │ │ └── JavaccAppSettingsService.kt │ │ ├── JavaccLanguage.kt │ │ ├── JavaccFileType.kt │ │ └── JavaccPairedBraceMatcher.kt └── test │ ├── resources │ └── com │ │ └── github │ │ └── oowekyala │ │ └── ijcc │ │ └── lang │ │ ├── parser │ │ └── fixtures │ │ │ ├── 21 │ │ │ └── Simple.ccc │ │ │ ├── ExpansionFails.jjt │ │ │ ├── RegexPrecedence.jjt │ │ │ ├── Assignments.jjt │ │ │ ├── Annotations.jjt │ │ │ ├── JjtreeStuff.jjt │ │ │ ├── TokenFail.jjt │ │ │ ├── JavaTypes.jjt │ │ │ ├── Lookaheads.jjt │ │ │ ├── Parentheses.jjt │ │ │ └── Tokens.jjt │ │ ├── options │ │ └── fixtures │ │ │ ├── LookaheadOverride.jjt │ │ │ ├── PackageOverride.jjt │ │ │ └── InvalidOptionType.jjt │ │ ├── injection │ │ └── fixtures │ │ │ └── CommonTreeBuilderTest.jjt │ │ └── folding │ │ └── fixtures │ │ └── ParserActions.jjt │ └── kotlin │ └── com │ └── github │ └── oowekyala │ └── ijcc │ ├── ide │ ├── inspections │ │ ├── JccInspectionTestBase.kt │ │ ├── ActionsWithinLookaheadInspectionTest.kt │ │ ├── UnnamedRegexInspectionTest.kt │ │ ├── UnnecessaryParenthesesInspectionTest.kt │ │ ├── EmptyParserActionsInspectionTest.kt │ │ ├── ConsecutiveParserActionsInspectionTest.kt │ │ ├── JccInspectionSuppressorTest.kt │ │ ├── LoopInRegexInspectionTest.kt │ │ ├── RegexMayMatchEmptyStringInspectionTest.kt │ │ ├── UnnecessaryAngledBracesRegexInspectionTest.kt │ │ └── UnusedProductionInspectionTest.kt │ ├── highlight │ │ └── JccRichHighlightTest.kt │ └── refs │ │ └── ReferenceTest.kt │ └── lang │ ├── parser │ ├── Jcc21ParserTests.kt │ └── JccParserTests.kt │ ├── JccFoldingBuilderTest.kt │ ├── ParserTestDsl.kt │ ├── JccNodeExtensionsTest.kt │ ├── TestUtil.kt │ ├── psi │ ├── JccElementFactoryTest.kt │ └── stubs │ │ └── JccStubTest.kt │ └── util │ └── TestExtensions.kt ├── changelog.html ├── sandbox └── README.md ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── justfile ├── make_icons ├── .idea ├── codeStyles │ ├── codeStyleConfig.xml │ └── Project.xml ├── vcs.xml ├── compiler.xml ├── kotlinc.xml ├── .gitignore ├── misc.xml ├── checkstyle-idea.xml └── runConfigurations │ └── Run_plugin_in_sandbox.xml ├── gradle.properties ├── LICENSE ├── .github └── workflows │ └── build.yml └── do-release.sh /settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'intellij-javacc' 2 | -------------------------------------------------------------------------------- /demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oowekyala/intellij-javacc/HEAD/demo.gif -------------------------------------------------------------------------------- /src/main/resources/com/github/oowekyala/ijcc/optionDescriptions/JDK_VERSION.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /changelog.html: -------------------------------------------------------------------------------- 1 |

What's fixed: 2 |

5 | -------------------------------------------------------------------------------- /sandbox/README.md: -------------------------------------------------------------------------------- 1 | Launch the plugin in a sandbox IDE, in this directory: 2 | ``` 3 | ./gradlew runIde 4 | ``` 5 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oowekyala/intellij-javacc/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /justfile: -------------------------------------------------------------------------------- 1 | 2 | # Requires Java 11 3 | 4 | build: 5 | ./gradlew b 6 | 7 | alias b := build 8 | 9 | buildPlugin: 10 | ./gradlew buildPlugin 11 | 12 | 13 | -------------------------------------------------------------------------------- /make_icons: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | svgo -f "src/main/resources/com/github/oowekyala/ijcc/icons" \ 4 | -o "build/resources/main/com/github/oowekyala/ijcc/icons" 5 | 6 | -------------------------------------------------------------------------------- /src/main/resources/com/github/oowekyala/ijcc/optionDescriptions/jjtree/NODE_PACKAGE.html: -------------------------------------------------------------------------------- 1 | The package to generate the node classes into. The default for this is the parser package. -------------------------------------------------------------------------------- /src/main/resources/com/github/oowekyala/ijcc/optionDescriptions/jjtree/BUILD_NODE_FILES.html: -------------------------------------------------------------------------------- 1 | Generate sample implementations for SimpleNode and any other nodes used in the grammar. -------------------------------------------------------------------------------- /src/main/resources/com/github/oowekyala/ijcc/optionDescriptions/jjtree/MULTI.html: -------------------------------------------------------------------------------- 1 | Generate a multi mode parse tree. The default for this is false, generating a simple mode parse tree. -------------------------------------------------------------------------------- /src/main/resources/com/github/oowekyala/ijcc/optionDescriptions/jjtree/NODE_DEFAULT_VOID.html: -------------------------------------------------------------------------------- 1 | Instead of making each non-decorated production an indefinite node, make it void instead. -------------------------------------------------------------------------------- /.idea/codeStyles/codeStyleConfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/main/resources/com/github/oowekyala/ijcc/optionDescriptions/jjtree/NODE_PREFIX.html: -------------------------------------------------------------------------------- 1 | The prefix used to construct node class names from node identifiers 2 | in multi mode. The default for this is "AST". -------------------------------------------------------------------------------- /src/main/resources/com/github/oowekyala/ijcc/optionDescriptions/jjtree/NODE_SCOPE_HOOK.html: -------------------------------------------------------------------------------- 1 | Insert calls to user-defined parser methods on entry and exit 2 | of every node scope. See Node Scope Hooks above. -------------------------------------------------------------------------------- /.idea/kotlinc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /src/main/resources/com/github/oowekyala/ijcc/optionDescriptions/OUTPUT_DIRECTORY.html: -------------------------------------------------------------------------------- 1 | This is a string valued option whose default value is the current 2 | directory. This controls where output files are generated. -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | # Datasource local storage ignored files 5 | /dataSources/ 6 | /dataSources.local.xml 7 | # Editor-based HTTP Client requests 8 | /httpRequests/ 9 | -------------------------------------------------------------------------------- /src/main/resources/com/github/oowekyala/ijcc/optionDescriptions/jjtree/NODE_CLASS.html: -------------------------------------------------------------------------------- 1 | If set defines the name of a user-supplied class that will extend SimpleNode. 2 | Any tree nodes created will then be subclasses of NODE_CLASS. -------------------------------------------------------------------------------- /src/main/resources/com/github/oowekyala/ijcc/icons/jccFile.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/main/resources/com/github/oowekyala/ijcc/icons/jccFile_dark.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/main/resources/com/github/oowekyala/ijcc/optionDescriptions/jjtree/VISITOR.html: -------------------------------------------------------------------------------- 1 | Insert a jjtAccept() method in the node classes, and generate 2 | a visitor implementation with an entry for every node type used 3 | in the grammar. -------------------------------------------------------------------------------- /src/main/kotlin/com/github/oowekyala/ijcc/util/Constants.kt: -------------------------------------------------------------------------------- 1 | package com.github.oowekyala.ijcc.util 2 | 3 | /** 4 | * Path to the root resource dir of the app, with no trailing slash. 5 | */ 6 | const val ResourcePrefix = "/com/github/oowekyala/ijcc" -------------------------------------------------------------------------------- /src/main/resources/com/github/oowekyala/ijcc/optionDescriptions/GRAMMAR_ENCODING.html: -------------------------------------------------------------------------------- 1 | Return the file encoding (e.g., UTF-8, ISO_8859-1, MacRoman); this will return the 2 | file.encoding system property if no value was explicitly set 3 | -------------------------------------------------------------------------------- /src/main/resources/com/github/oowekyala/ijcc/optionDescriptions/jjtree/VISITOR_EXCEPTION.html: -------------------------------------------------------------------------------- 1 | If this option is set, it is used in the throws clause of the 2 | generated jjtAccept() methods and the visit() methods. -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.1.1-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /src/main/resources/com/github/oowekyala/ijcc/colorSchemes/Darcula_Javacc.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | -------------------------------------------------------------------------------- /src/main/resources/com/github/oowekyala/ijcc/colorSchemes/Default_Javacc.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | kotlin.code.style=official 2 | org.gradle.jvmargs=-Xmx2G 3 | #https://plugins.jetbrains.com/docs/intellij/using-kotlin.html#incremental-compilation 4 | kotlin.incremental.useClasspathSnapshot=false 5 | kotlin.stdlib.default.dependency = false 6 | -------------------------------------------------------------------------------- /src/main/resources/com/github/oowekyala/ijcc/optionDescriptions/ERROR_REPORTING.html: -------------------------------------------------------------------------------- 1 | Setting this option to false causes errors due to parse errors to be reported 2 | in somewhat less detail. The only reason to set this option to 3 | false is to improve performance. 4 | -------------------------------------------------------------------------------- /src/main/resources/com/github/oowekyala/ijcc/optionDescriptions/jjtree/VISITOR_DATA_TYPE.html: -------------------------------------------------------------------------------- 1 | If this option is set, it is used in the signature of the generated 2 | jjtAccept() methods and the visit() methods 3 | as the type of the data argument. 4 | -------------------------------------------------------------------------------- /src/main/resources/com/github/oowekyala/ijcc/optionDescriptions/jjtree/VISITOR_RETURN_TYPE.html: -------------------------------------------------------------------------------- 1 | If this option is set, it is used in the signature of the generated 2 | jjtAccept() methods and the visit() methods 3 | as the return type of the method. -------------------------------------------------------------------------------- /src/main/kotlin/com/github/oowekyala/ijcc/lang/lexer/JavaccLexerAdapter.kt: -------------------------------------------------------------------------------- 1 | package com.github.oowekyala.ijcc.lang.lexer 2 | 3 | import com.intellij.lexer.FlexAdapter 4 | class JavaccLexerAdapter @JvmOverloads constructor(isCCC: Boolean = false) : FlexAdapter(JavaccLexer(isCCC)) 5 | -------------------------------------------------------------------------------- /src/main/kotlin/com/github/oowekyala/ijcc/lang/model/AccessModifier.kt: -------------------------------------------------------------------------------- 1 | package com.github.oowekyala.ijcc.lang.model 2 | 3 | /** 4 | * @author Clément Fournier 5 | */ 6 | enum class AccessModifier { 7 | PRIVATE, 8 | PACKAGE_LOCAL, 9 | PROTECTED, 10 | PUBLIC 11 | } 12 | -------------------------------------------------------------------------------- /src/main/resources/com/github/oowekyala/ijcc/icons/jjtreeNode.svg: -------------------------------------------------------------------------------- 1 | n -------------------------------------------------------------------------------- /src/main/resources/com/github/oowekyala/ijcc/optionDescriptions/jjtree/NODE_EXTENDS.html: -------------------------------------------------------------------------------- 1 | The superclass for the SimpleNode class. By providing a custom 2 | superclass you may be able to avoid the need to edit the generated 3 | SimpleNode.java. See the examples/Interpreter for an example 4 | usage. -------------------------------------------------------------------------------- /src/main/resources/com/github/oowekyala/ijcc/optionDescriptions/SUPPORT_CLASS_VISIBILITY_PUBLIC.html: -------------------------------------------------------------------------------- 1 | The default action is to generate support classes (such as Token.java, ParseException.java 2 | etc) with public visibility. If set to false, the classes will 3 | be generated with package-private visibility. -------------------------------------------------------------------------------- /src/main/resources/com/github/oowekyala/ijcc/optionDescriptions/jjtree/JJTREE_OUTPUT_DIRECTORY.html: -------------------------------------------------------------------------------- 1 | By default, JJTree generates its output in the directory specified 2 | in the global {option_link OUTPUT_DIRECTORY} setting. Explicitly setting this 3 | option allows the user to separate the parser from the tree 4 | files. -------------------------------------------------------------------------------- /src/main/resources/com/github/oowekyala/ijcc/optionDescriptions/BUILD_PARSER.html: -------------------------------------------------------------------------------- 1 | By default JavaCC generates a Java parser file. When set to false, 2 | the parser file is not generated. 3 |

Typically, this option is set to false when you wish to generate 4 | only the token manager and use it without the associated parser. 5 | -------------------------------------------------------------------------------- /src/main/resources/com/github/oowekyala/ijcc/optionDescriptions/jjtree/NODE_USES_PARSER.html: -------------------------------------------------------------------------------- 1 | JJTree will use an alternate form of the node construction routines 2 | where it passes the parser object in. For example, 3 |

4 |      public static Node MyNode.jjtCreate(MyParser p, int id);
5 | 
6 |      MyNode(MyParser p, int id);
7 | 
8 | -------------------------------------------------------------------------------- /src/main/resources/META-INF/MANIFEST.mf: -------------------------------------------------------------------------------- 1 | Manifest-Version: 1.0 2 | Class-Path: light-psi-all.jar lib/annotations.jar lib/asm-all-7.0.1.ja 3 | r lib/automaton-1.12-1.jar lib/extensions.jar lib/guava-25.1-jre.jar 4 | lib/idea.jar lib/jdom.jar lib/picocontainer-1.2.jar lib/platform-api. 5 | jar lib/platform-impl.jar lib/trove4j.jar lib/util.jar 6 | -------------------------------------------------------------------------------- /src/main/resources/com/github/oowekyala/ijcc/optionDescriptions/DEBUG_LOOKAHEAD.html: -------------------------------------------------------------------------------- 1 | Setting this option to true causes the parser to generate all the tracing 2 | information it does when the option {option_link DEBUG_PARSER} 3 | is true, and in addition, also causes it to generated a trace of actions performed 4 | during lookahead operation. 5 | -------------------------------------------------------------------------------- /src/main/resources/com/github/oowekyala/ijcc/optionDescriptions/TOKEN_EXTENDS.html: -------------------------------------------------------------------------------- 1 | This is a string option whose default value is "", meaning that 2 | the generated Token class will extend java.lang.Object. This 3 | option may be set to the name of a class that will be used as 4 | the base class for the generated Token class. -------------------------------------------------------------------------------- /src/main/resources/com/github/oowekyala/ijcc/optionDescriptions/UNICODE_INPUT.html: -------------------------------------------------------------------------------- 1 | When set to true, the generated parser uses uses an input stream object 2 | that reads Unicode files. By default, ASCII files are assumed. 3 | 4 |

This option is ignored if either of options {option_link USER_TOKEN_MANAGER}, 5 | {option_link USER_CHAR_STREAM} is set to true. 6 | -------------------------------------------------------------------------------- /src/main/resources/com/github/oowekyala/ijcc/optionDescriptions/TOKEN_FACTORY.html: -------------------------------------------------------------------------------- 1 | This is a string option whose default value is "", meaning that 2 | Tokens will be created by calling Token.newToken(). If set the 3 | option names a Token factory class containing a public static 4 | Token newToken(int ofKind, String image) method. -------------------------------------------------------------------------------- /src/main/kotlin/com/github/oowekyala/ijcc/lang/psi/IJccElementType.kt: -------------------------------------------------------------------------------- 1 | package com.github.oowekyala.ijcc.lang.psi 2 | 3 | import com.github.oowekyala.ijcc.JavaccLanguage 4 | import com.intellij.psi.tree.IElementType 5 | 6 | /** 7 | * @author Clément Fournier 8 | * @since 1.0 9 | */ 10 | class IJccElementType(id: String) : IElementType(id, JavaccLanguage.INSTANCE) 11 | -------------------------------------------------------------------------------- /src/main/kotlin/com/github/oowekyala/ijcc/lang/psi/IJccTokenType.kt: -------------------------------------------------------------------------------- 1 | package com.github.oowekyala.ijcc.lang.psi 2 | 3 | import com.github.oowekyala.ijcc.JavaccLanguage 4 | import com.intellij.psi.tree.IElementType 5 | 6 | /** 7 | * @author Clément Fournier 8 | * @since 1.0 9 | */ 10 | class IJccTokenType(val id: String) : IElementType(id, JavaccLanguage.INSTANCE) 11 | -------------------------------------------------------------------------------- /src/main/resources/com/github/oowekyala/ijcc/optionDescriptions/USER_TOKEN_MANAGER.html: -------------------------------------------------------------------------------- 1 | By default JavaCC generates a token manager that works on the specified grammar tokens. 2 | If this option is set to true, then the parser is generated to accept tokens from any 3 | token manager of type "TokenManager" - that interface is generated into the generated 4 | parser directory. 5 | -------------------------------------------------------------------------------- /src/main/resources/com/github/oowekyala/ijcc/optionDescriptions/OTHER_AMBIGUITY_CHECK.html: -------------------------------------------------------------------------------- 1 | This is the number of tokens considered in checking all other kinds of choices 2 | (i.e., of the forms (A)*, (A)+, and (A)?) for ambiguity. 3 | This takes more time to do than the choice checking, and hence 4 | the default value is set to 1 rather than 2. 5 | -------------------------------------------------------------------------------- /src/main/resources/com/github/oowekyala/ijcc/optionDescriptions/jjtree/TRACK_TOKENS.html: -------------------------------------------------------------------------------- 1 | Insert jjtGetFirstToken(), jjtSetFirstToken(), getLastToken(), 2 | and jjtSetLastToken() methods in SimpleNode. The FirstToken 3 | is automatically set up on entry to a node scope; the LastToken 4 | is automatically set up on exit from a node scope. -------------------------------------------------------------------------------- /src/test/resources/com/github/oowekyala/ijcc/lang/parser/fixtures/ExpansionFails.jjt: -------------------------------------------------------------------------------- 1 | PARSER_BEGIN(JJTreeParser) 2 | 3 | package org.javacc.jjtree; 4 | /** 5 | * This is my parser declaration 6 | */ 7 | public class JJTreeParser { 8 | 9 | } 10 | 11 | PARSER_END(JJTreeParser) 12 | 13 | 14 | 15 | void emptyAlt(): {} 16 | { 17 | a() | b() | | c() 18 | } -------------------------------------------------------------------------------- /src/main/kotlin/com/github/oowekyala/ijcc/lang/model/RegexKind.kt: -------------------------------------------------------------------------------- 1 | package com.github.oowekyala.ijcc.lang.model 2 | 3 | import java.util.* 4 | 5 | enum class RegexKind { 6 | TOKEN, SKIP, SPECIAL_TOKEN, MORE; 7 | 8 | companion object { 9 | val All: Set = EnumSet.allOf(RegexKind::class.java) 10 | val JustToken: Set = EnumSet.of(TOKEN) 11 | } 12 | } -------------------------------------------------------------------------------- /src/main/resources/com/github/oowekyala/ijcc/optionDescriptions/JAVA_TEMPLATE_TYPE.html: -------------------------------------------------------------------------------- 1 | Style of java code generation. 2 |

-------------------------------------------------------------------------------- /src/main/resources/com/github/oowekyala/ijcc/icons/tree.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/main/resources/com/github/oowekyala/ijcc/optionDescriptions/BUILD_TOKEN_MANAGER.html: -------------------------------------------------------------------------------- 1 | By default JavaCC generates a token manager class. When set to 2 | false the token manager file is not generated. 3 | 4 |

The only reason to set this option to false is to save some time during parser generation when you fix problems 5 | in the parser part of the grammar file and leave the lexical 6 | specifications untouched. -------------------------------------------------------------------------------- /src/main/resources/com/github/oowekyala/ijcc/optionDescriptions/IGNORE_CASE.html: -------------------------------------------------------------------------------- 1 | Setting this option to true causes the generated token manager to ignore 2 | case in the token specifications and the input files. This is 3 | useful for writing grammars for languages such as HTML. It is 4 | also possible to localize the effect of IGNORE_CASE by adding 5 | the [IGNORE_CASE] annotation on regex productions. 6 | -------------------------------------------------------------------------------- /src/main/resources/com/github/oowekyala/ijcc/optionDescriptions/LOOKAHEAD.html: -------------------------------------------------------------------------------- 1 | The number of tokens to look ahead before making a decision 2 | at a choice point during parsing. 3 |

The smaller this number, the faster the parser. This number 4 | may be overridden for specific productions within the grammar 5 | as described later. See the description of the lookahead algorithm 6 | for complete details on how lookahead works. -------------------------------------------------------------------------------- /src/main/kotlin/com/github/oowekyala/ijcc/ide/refs/PsiEltResolveResult.kt: -------------------------------------------------------------------------------- 1 | package com.github.oowekyala.ijcc.ide.refs 2 | 3 | import com.intellij.psi.PsiElement 4 | import com.intellij.psi.ResolveResult 5 | 6 | data class PsiEltResolveResult(private val myElt: T) : 7 | ResolveResult { 8 | override fun getElement(): T = myElt 9 | 10 | override fun isValidResult(): Boolean = true 11 | } -------------------------------------------------------------------------------- /src/main/kotlin/com/github/oowekyala/ijcc/lang/psi/stubs/StubExtensions.kt: -------------------------------------------------------------------------------- 1 | package com.github.oowekyala.ijcc.lang.psi.stubs 2 | 3 | import com.intellij.psi.stubs.StubElement 4 | 5 | /** 6 | * @author Clément Fournier 7 | * @since 1.2 8 | */ 9 | 10 | 11 | fun StubElement<*>.ancestors(includeSelf: Boolean): Sequence> = 12 | generateSequence(if (includeSelf) this else parentStub) { it.parentStub } -------------------------------------------------------------------------------- /src/main/resources/com/github/oowekyala/ijcc/optionDescriptions/TOKEN_MANAGER_USES_PARSER.html: -------------------------------------------------------------------------------- 1 | When set to true, the generated token manager will include a field called 2 | parser that references the instantiating parser instance. The 3 | main reason for having a parser in a token manager is using some of its 4 | logic in lexical actions. This option has no effect if the {option_link STATIC} option is set 5 | to true. -------------------------------------------------------------------------------- /src/main/resources/com/github/oowekyala/ijcc/fileTemplates/JavaCC grammar.jj.ft: -------------------------------------------------------------------------------- 1 | /* 2 | * Grammar for the ${NAME} language. 3 | * 4 | * Author: ${USER} 5 | * Date: ${YEAR}-${MONTH}-${DAY} 6 | * 7 | */ 8 | 9 | options { 10 | // option=value 11 | } 12 | 13 | PARSER_BEGIN(${NAME}) 14 | 15 | class ${NAME}Parser { 16 | 17 | 18 | } 19 | 20 | PARSER_END(${NAME}) 21 | 22 | void root(): 23 | {} 24 | { 25 | 26 | } 27 | -------------------------------------------------------------------------------- /src/main/resources/com/github/oowekyala/ijcc/optionDescriptions/JAVA_UNICODE_ESCAPE.html: -------------------------------------------------------------------------------- 1 | When set to true, the generated parser uses an input stream object that 2 | processes Java Unicode escapes (\u...) before sending characters 3 | to the token manager. By default, Java Unicode escapes are not 4 | processed. 5 | 6 |

This option is ignored if either of options {option_link USER_TOKEN_MANAGER}, 7 | {option_link USER_CHAR_STREAM} is set to true. 8 | -------------------------------------------------------------------------------- /src/main/kotlin/com/github/oowekyala/ijcc/lang/psi/JccOptionBinding.kt: -------------------------------------------------------------------------------- 1 | package com.github.oowekyala.ijcc.lang.psi 2 | 3 | interface JccOptionBinding : JccIdentifierOwner { 4 | 5 | val optionValue: JccOptionValue? 6 | 7 | override fun getName(): String 8 | 9 | /** 10 | * Nullable because it could be e.g. a LOOKAHEAD token. 11 | * Use [getName] directly. 12 | */ 13 | override fun getNameIdentifier(): JccIdentifier? 14 | 15 | } -------------------------------------------------------------------------------- /src/test/resources/com/github/oowekyala/ijcc/lang/parser/fixtures/RegexPrecedence.jjt: -------------------------------------------------------------------------------- 1 | PARSER_BEGIN(JJTreeParser) 2 | 3 | package org.javacc.jjtree; 4 | /** 5 | * This is my parser declaration 6 | */ 7 | public class JJTreeParser { 8 | 9 | } 10 | 11 | PARSER_END(JJTreeParser) 12 | 13 | 14 | 15 | void named(): {} 16 | { 17 | < foo: 18 | } 19 | 20 | void ref(): {} 21 | { 22 | < foo 23 | } 24 | void inline(): {} 25 | { 26 | < 27 | } -------------------------------------------------------------------------------- /src/main/kotlin/com/github/oowekyala/ijcc/lang/psi/DepthFirstVisitor.kt: -------------------------------------------------------------------------------- 1 | package com.github.oowekyala.ijcc.lang.psi 2 | 3 | import com.intellij.psi.PsiElement 4 | import com.intellij.psi.PsiRecursiveVisitor 5 | 6 | /** 7 | * @author Clément Fournier 8 | * @since 1.0 9 | */ 10 | open class DepthFirstVisitor : JccVisitor(), PsiRecursiveVisitor { 11 | 12 | override fun visitElement(element: PsiElement) { 13 | element.acceptChildren(this) 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /src/main/resources/com/github/oowekyala/ijcc/optionDescriptions/DEBUG_PARSER.html: -------------------------------------------------------------------------------- 1 | This option is used to obtain debugging information from the generated parser. 2 | Setting this option to true causes the parser to generate a trace 3 | of its actions. Tracing may be disabled by calling the method 4 | disable_tracing() in the generated parser class. Tracing may 5 | be subsequently enabled by calling the method enable_tracing() 6 | in the generated parser class. 7 | -------------------------------------------------------------------------------- /src/main/resources/com/github/oowekyala/ijcc/icons/terminal.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/main/resources/com/github/oowekyala/ijcc/optionDescriptions/CACHE_TOKENS.html: -------------------------------------------------------------------------------- 1 | Setting this option to true causes the generated parser to lookahead 2 | for extra tokens ahead of time. This facilitates some performance 3 | improvements. However, in this case (when the option is true), 4 | interactive applications may not work since the parser needs 5 | to work synchronously with the availability of tokens from the 6 | input stream. In such cases, it's best to leave this option at 7 | its default value. -------------------------------------------------------------------------------- /src/main/kotlin/com/github/oowekyala/ijcc/ide/intentions/JccIntentionBase.kt: -------------------------------------------------------------------------------- 1 | package com.github.oowekyala.ijcc.ide.intentions 2 | 3 | import com.intellij.codeInsight.intention.PsiElementBaseIntentionAction 4 | 5 | /** 6 | * @author Clément Fournier 7 | * @since 1.0 8 | */ 9 | abstract class JccIntentionBase(val name: String) : PsiElementBaseIntentionAction() { 10 | 11 | override fun getFamilyName(): String = name 12 | 13 | override fun getText(): String = name 14 | 15 | } 16 | 17 | -------------------------------------------------------------------------------- /src/test/resources/com/github/oowekyala/ijcc/lang/options/fixtures/LookaheadOverride.jjt: -------------------------------------------------------------------------------- 1 | options { 2 | LOOKAHEAD = 4; 3 | MULTI = true; 4 | } 5 | 6 | PARSER_BEGIN(JJTreeParser) 7 | 8 | package org.javacc.jjtree; 9 | /** 10 | * This is my parser declaration 11 | */ 12 | public class JJTreeParser { 13 | 14 | void jjtreeOpenNodeScope(Node n) { 15 | ((JJTreeNode)n).setFirstToken(getToken(1)); 16 | } 17 | 18 | } 19 | 20 | PARSER_END(JJTreeParser) 21 | 22 | 23 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /src/main/resources/com/github/oowekyala/ijcc/optionDescriptions/DEBUG_TOKEN_MANAGER.html: -------------------------------------------------------------------------------- 1 | This option is used to obtain debugging information from the 2 | generated token manager. Setting this option to true causes the 3 | token manager to generate a trace of its actions. This trace is rather large 4 | and should only be used when you have a lexical error that has 5 | been reported to you and you cannot understand why. Typically, 6 | in this situation, you can determine the problem by looking at 7 | the last few lines of this trace. 8 | -------------------------------------------------------------------------------- /src/test/resources/com/github/oowekyala/ijcc/lang/parser/fixtures/Assignments.jjt: -------------------------------------------------------------------------------- 1 | PARSER_BEGIN(JJTreeParser) 2 | 3 | package org.javacc.jjtree; 4 | /** 5 | * This is my parser declaration 6 | */ 7 | public class JJTreeParser { 8 | 9 | } 10 | 11 | PARSER_END(JJTreeParser) 12 | 13 | 14 | 15 | void assignments(): {} 16 | { 17 | a= 18 | a="h" 19 | a=< f: "olol"> 20 | c=foo() 21 | // invalid parses 22 | a=("f") 23 | a=[h()] 24 | a=try {foo()}catch(Foo f){} 25 | } -------------------------------------------------------------------------------- /src/main/resources/META-INF/pluginIcon.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/test/resources/com/github/oowekyala/ijcc/lang/parser/fixtures/Annotations.jjt: -------------------------------------------------------------------------------- 1 | PARSER_BEGIN(JJTreeParser) 2 | public class JJTreeParser { } 3 | PARSER_END(JJTreeParser) 4 | 5 | // note: for now annotations on productions is not supported by our parser, 6 | // not sure if the javacc syntax allows that 7 | 8 | void p1(@Nullable T foo, List<@Nullable T> list): {} { "a" } 9 | void p2(T foo, @A List<@Nullable T> list): {} { "a" } 10 | void p2(T foo, @A List list): {} { "a" } 11 | @O int p2(): {} { "a" } 12 | -------------------------------------------------------------------------------- /src/main/resources/com/github/oowekyala/ijcc/icons/jccNavigateToGrammar.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/main/resources/com/github/oowekyala/ijcc/icons/jccNavigateToGrammar_dark.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/test/resources/com/github/oowekyala/ijcc/lang/options/fixtures/PackageOverride.jjt: -------------------------------------------------------------------------------- 1 | options { 2 | LOOKAHEAD = 4; 3 | NODE_PACKAGE = "org.foo"; 4 | MULTI = true; 5 | } 6 | 7 | PARSER_BEGIN(JJTreeParser) 8 | 9 | package org.javacc.jjtree; 10 | /** 11 | * This is my parser declaration 12 | */ 13 | public class JJTreeParser { 14 | 15 | void jjtreeOpenNodeScope(Node n) { 16 | ((JJTreeNode)n).setFirstToken(getToken(1)); 17 | } 18 | 19 | } 20 | 21 | PARSER_END(JJTreeParser) 22 | 23 | 24 | -------------------------------------------------------------------------------- /src/test/resources/com/github/oowekyala/ijcc/lang/options/fixtures/InvalidOptionType.jjt: -------------------------------------------------------------------------------- 1 | options { 2 | LOOKAHEAD = "60"; 3 | NODE_DEFAULT_VOID = "true"; 4 | NODE_PACKAGE = 6; 5 | } 6 | 7 | PARSER_BEGIN(JJTreeParser) 8 | 9 | package org.javacc.jjtree; 10 | /** 11 | * This is my parser declaration 12 | */ 13 | public class JJTreeParser { 14 | 15 | void jjtreeOpenNodeScope(Node n) { 16 | ((JJTreeNode)n).setFirstToken(getToken(1)); 17 | } 18 | 19 | } 20 | 21 | PARSER_END(JJTreeParser) 22 | 23 | 24 | -------------------------------------------------------------------------------- /src/test/resources/com/github/oowekyala/ijcc/lang/parser/fixtures/21/Simple.ccc: -------------------------------------------------------------------------------- 1 | 2 | INJECT(Foo): {} {} 3 | INJECT(class Foo): {} {} 4 | INJECT(interface Foo): {} {} 5 | 6 | INJECT(Foo) {} {} 7 | INJECT(class Foo) {} {} 8 | INJECT(interface Foo) {} {} 9 | 10 | INCLUDE(Java) 11 | INCLUDE(Java) :{} 12 | INCLUDE("Java") 13 | INCLUDE("Java") :{} 14 | INCLUDE("Java" 15 | INCLUDE( 16 | INCLUDE 17 | 18 | // errors 19 | INJECT(interface Foo) {} 20 | INJECT(interface Foo): 21 | 22 | INJECT:{} 23 | INJECT {} 24 | 25 | 26 | void assignments(): {} 27 | { 28 | "&&" 29 | } 30 | -------------------------------------------------------------------------------- /src/test/resources/com/github/oowekyala/ijcc/lang/injection/fixtures/CommonTreeBuilderTest.jjt: -------------------------------------------------------------------------------- 1 | PARSER_BEGIN(JJTreeParser) 2 | 3 | PARSER_END(JJTreeParser) 4 | 5 | 6 | 7 | void test1() #OptionBinding : 8 | {} 9 | { 10 | "ff" {jjtThis.foo();} 11 | } 12 | 13 | void test2() #OptionBinding : 14 | {} 15 | { 16 | "ff" "cd" 17 | } 18 | 19 | void test2() #OptionBinding : 20 | {} 21 | { 22 | "ff" {jjtThis.foo();} | "cd" {jjtThis.bar();} 23 | } 24 | 25 | void test2() #OptionBinding : 26 | {} 27 | { 28 | LOOKAHEAD({ jjtThis.bar() }) 29 | "ff" | "cd" | "f" "c" 30 | } 31 | -------------------------------------------------------------------------------- /src/test/kotlin/com/github/oowekyala/ijcc/ide/inspections/JccInspectionTestBase.kt: -------------------------------------------------------------------------------- 1 | package com.github.oowekyala.ijcc.ide.inspections 2 | 3 | import com.github.oowekyala.ijcc.util.JccAnnotationTestBase 4 | import com.intellij.psi.PsiFile 5 | 6 | abstract class JccInspectionTestBase( 7 | private val inspection: JccInspectionBase 8 | ) : JccAnnotationTestBase() { 9 | 10 | override fun configureByText(text: String): PsiFile = 11 | super.configureByText(text) 12 | .also { 13 | myFixture.enableInspections(inspection) 14 | } 15 | } -------------------------------------------------------------------------------- /src/main/resources/com/github/oowekyala/ijcc/icons/jjtreeFile.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/main/resources/com/github/oowekyala/ijcc/optionDescriptions/COMMON_TOKEN_ACTION.html: -------------------------------------------------------------------------------- 1 | When set to true, every call to the token manager's method getNextToken 2 | (see the description of the Java Compiler Compiler API) will 3 | cause a call to a used defined method CommonTokenAction after 4 | the token has been scanned in by the token manager. The user 5 | must define this method within the TOKEN_MGR_DECLS section. The 6 | signature of this method is: 7 |

8 |     void CommonTokenAction(Token t)
9 | 
-------------------------------------------------------------------------------- /src/main/resources/com/github/oowekyala/ijcc/optionDescriptions/USER_CHAR_STREAM.html: -------------------------------------------------------------------------------- 1 | By default JavaCC generates a character stream reader as specified 2 | by the options JAVA_UNICODE_ESCAPE and UNICODE_INPUT. The generated 3 | token manager receives characters from this stream reader. If 4 | this option is set to true, then the token manager is generated 5 | to read characters from any character stream reader of type "CharStream.java". 6 | That file is generated into the generated parser directory. 7 | 8 |

This option is ignored if {option_link USER_TOKEN_MANAGER} is set to true. 9 | -------------------------------------------------------------------------------- /src/main/resources/com/github/oowekyala/ijcc/icons/jjtreeFile_dark.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/main/resources/com/github/oowekyala/ijcc/optionDescriptions/SANITY_CHECK.html: -------------------------------------------------------------------------------- 1 | Set this option to false to disable some semantic checks when building. 2 | JavaCC performs many syntactic and semantic checks on the grammar file 3 | during parser generation. Some checks such as detection of left 4 | recursion, detection of ambiguity, and bad usage of empty expansions 5 | may be suppressed for faster parser generation. Note that the presence of these errors (even 6 | if they are not detected and reported by setting this option 7 | to false) can cause unexpected behavior from the generated parser 8 | -------------------------------------------------------------------------------- /src/main/kotlin/com/github/oowekyala/ijcc/lang/psi/JccNonTerminalProduction.kt: -------------------------------------------------------------------------------- 1 | // This is a generated file. Not intended for manual editing. 2 | package com.github.oowekyala.ijcc.lang.psi 3 | 4 | interface JccNonTerminalProduction 5 | : JccIdentifierOwner, JjtNodeClassOwner, JccProduction { 6 | 7 | val javaBlock: JccJavaBlock? 8 | 9 | override val jjtreeNodeDescriptor: JccJjtreeNodeDescriptor? 10 | 11 | val header: JccJavaNonTerminalProductionHeader 12 | 13 | override fun getNameIdentifier(): JccIdentifier 14 | 15 | override fun getName(): String 16 | 17 | } 18 | -------------------------------------------------------------------------------- /src/main/resources/com/github/oowekyala/ijcc/optionDescriptions/jjtree/NODE_FACTORY.html: -------------------------------------------------------------------------------- 1 | Specify a class containing a factory method with following signature to construct nodes: 2 |

 3 |      public static Node jjtCreate(int id)
 4 | 
5 | 6 |

For backwards compatibility, the value true may also be specified, 7 | meaning that the node class will be used as the factory class, for 8 | each node. This is equivalent to using the string "*" 9 | as a value. 10 | 11 |

If the value is an empty string (or default), the constructor of the 12 | node class will be used instead. 13 | -------------------------------------------------------------------------------- /src/main/kotlin/com/github/oowekyala/ijcc/ide/highlight/JavaccSyntaxHighlighterFactory.kt: -------------------------------------------------------------------------------- 1 | package com.github.oowekyala.ijcc.ide.highlight 2 | 3 | import com.intellij.openapi.fileTypes.SyntaxHighlighter 4 | import com.intellij.openapi.fileTypes.SyntaxHighlighterFactory 5 | import com.intellij.openapi.project.Project 6 | import com.intellij.openapi.vfs.VirtualFile 7 | 8 | /** Extension point. */ 9 | class JavaccSyntaxHighlighterFactory : SyntaxHighlighterFactory() { 10 | override fun getSyntaxHighlighter(project: Project?, virtualFile: VirtualFile?): SyntaxHighlighter = 11 | JavaccSyntaxHighlighter() 12 | } -------------------------------------------------------------------------------- /src/test/resources/com/github/oowekyala/ijcc/lang/parser/fixtures/JjtreeStuff.jjt: -------------------------------------------------------------------------------- 1 | PARSER_BEGIN(JJTreeParser) 2 | 3 | package org.javacc.jjtree; 4 | /** 5 | * This is my parser declaration 6 | */ 7 | public class JJTreeParser { 8 | 9 | } 10 | 11 | PARSER_END(JJTreeParser) 12 | 13 | 14 | void parens() #Bar : 15 | {} 16 | { 17 | ("foo" "bar") #Foo 18 | } 19 | 20 | void parens2() #Bar : 21 | {} 22 | { 23 | "a" | ("foo" "bar") #Foo 24 | } 25 | 26 | void parens3() #Bar : 27 | {} 28 | { 29 | "a" 30 | } 31 | 32 | void parens4() #Bar : 33 | {} 34 | { 35 | "a" "b" 36 | } 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /src/main/kotlin/com/github/oowekyala/ijcc/lang/psi/JccIdentifierOwner.kt: -------------------------------------------------------------------------------- 1 | package com.github.oowekyala.ijcc.lang.psi 2 | 3 | import com.intellij.psi.PsiElement 4 | import com.intellij.psi.PsiNameIdentifierOwner 5 | 6 | /** 7 | * An element that has an identifier. 8 | * 9 | * @author Clément Fournier 10 | * @since 1.0 11 | */ 12 | interface JccIdentifierOwner : JccPsiElement, PsiNameIdentifierOwner { 13 | 14 | override fun getNameIdentifier(): JccIdentifier? 15 | 16 | override fun setName(name: String): PsiElement { 17 | nameIdentifier?.name = name 18 | return this 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /src/main/resources/com/github/oowekyala/ijcc/icons/jjtreeNodeLocate.svg: -------------------------------------------------------------------------------- 1 | n -------------------------------------------------------------------------------- /src/main/resources/com/github/oowekyala/ijcc/icons/jjtreeNodeLocate_dark.svg: -------------------------------------------------------------------------------- 1 | n -------------------------------------------------------------------------------- /src/main/resources/com/github/oowekyala/ijcc/icons/jjtreeNode_alt.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/test/resources/com/github/oowekyala/ijcc/lang/parser/fixtures/TokenFail.jjt: -------------------------------------------------------------------------------- 1 | PARSER_BEGIN(JJTreeParser) 2 | 3 | package org.javacc.jjtree; 4 | /** 5 | * This is my parser declaration 6 | */ 7 | public class JJTreeParser { 8 | 9 | } 10 | 11 | PARSER_END(JJTreeParser) 12 | 13 | 14 | TOKEN : 15 | { 16 | < LEFT_WILDCARD : "*:" > 17 | | < RIGHT_WILDCARD : ":*" > 18 | | 19 | // The actual lexical grammar for NCName is: any name except * ":" * 20 | < NCNAME: "foo" > 21 | | 22 | fo 23 | 24 | } 25 | 26 | 27 | void lookaheads(): {} 28 | { 29 | "fo" 30 | 31 | } 32 | 33 | -------------------------------------------------------------------------------- /src/main/kotlin/com/github/oowekyala/ijcc/lang/psi/JccNodeExtensions.kt: -------------------------------------------------------------------------------- 1 | package com.github.oowekyala.ijcc.lang.psi 2 | 3 | import com.github.oowekyala.ijcc.lang.model.AccessModifier 4 | 5 | /* 6 | Miscellaneous semantic extensions for jcc nodes. 7 | */ 8 | 9 | 10 | 11 | val JccJavaAccessModifier.modelConstant: AccessModifier 12 | get() = when (text) { 13 | "" -> AccessModifier.PACKAGE_LOCAL 14 | "public" -> AccessModifier.PUBLIC 15 | "protected" -> AccessModifier.PROTECTED 16 | "private" -> AccessModifier.PRIVATE 17 | else -> throw IllegalStateException() 18 | } 19 | -------------------------------------------------------------------------------- /src/main/kotlin/com/github/oowekyala/ijcc/lang/model/BaseCachedModelObject.kt: -------------------------------------------------------------------------------- 1 | package com.github.oowekyala.ijcc.lang.model 2 | 3 | import com.github.oowekyala.ijcc.lang.psi.JccFile 4 | 5 | /** 6 | * @author Clément Fournier 7 | * @since 1.2 8 | */ 9 | abstract class BaseCachedModelObject(val file: JccFile) { 10 | 11 | 12 | final override fun equals(other: Any?): Boolean = 13 | if (other?.javaClass != javaClass || other !is BaseCachedModelObject) false 14 | else file.virtualFile.path.hashCode() == other.file.virtualFile.path.hashCode() 15 | 16 | final override fun hashCode(): Int = file.virtualFile.path.hashCode() 17 | } 18 | -------------------------------------------------------------------------------- /.idea/codeStyles/Project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 10 | 11 | 13 | 14 | -------------------------------------------------------------------------------- /src/main/resources/com/github/oowekyala/ijcc/optionDescriptions/FORCE_LA_CHECK.html: -------------------------------------------------------------------------------- 1 | This option setting controls lookahead ambiguity checking performed by JavaCC. 2 | By default (when this option is false), lookahead ambiguity checking 3 | is performed for all choice points where the default lookahead 4 | of 1 is used. Lookahead ambiguity checking is not performed at 5 | choice points where there is an explicit lookahead specification, 6 | or if the option {option_link LOOKAHEAD} is set to something other than 1. 7 | Setting this option to true performs lookahead ambiguity checking 8 | at all choice points regardless of the lookahead specifications 9 | in the grammar file. -------------------------------------------------------------------------------- /src/main/kotlin/com/github/oowekyala/ijcc/lang/psi/stubs/JccStubElementType.kt: -------------------------------------------------------------------------------- 1 | package com.github.oowekyala.ijcc.lang.psi.stubs 2 | 3 | import com.github.oowekyala.ijcc.JavaccLanguage 4 | import com.github.oowekyala.ijcc.lang.psi.JccPsiElement 5 | import com.intellij.psi.stubs.IStubElementType 6 | import com.intellij.psi.stubs.StubElement 7 | 8 | /** 9 | * @author Clément Fournier 10 | * @since 1.2 11 | */ 12 | abstract class JccStubElementType, TElem : JccPsiElement>(id: String) : 13 | IStubElementType(id, JavaccLanguage.INSTANCE) { 14 | 15 | final override fun getExternalId(): String = "javacc.${super.toString()}" 16 | } 17 | -------------------------------------------------------------------------------- /.idea/checkstyle-idea.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 15 | 16 | -------------------------------------------------------------------------------- /src/main/kotlin/com/github/oowekyala/ijcc/lang/psi/JccPsiElement.kt: -------------------------------------------------------------------------------- 1 | package com.github.oowekyala.ijcc.lang.psi 2 | 3 | import com.github.oowekyala.ijcc.lang.model.IGrammarOptions 4 | import com.intellij.psi.NavigatablePsiElement 5 | 6 | /** 7 | * Top-level interface for all javacc psi element. 8 | * 9 | * @author Clément Fournier 10 | * @since 1.0 11 | */ 12 | interface JccPsiElement : NavigatablePsiElement { 13 | 14 | override fun getContainingFile(): JccFile 15 | 16 | /** Gets the options bundle associated with the grammar this element is found in. */ 17 | val grammarOptions: IGrammarOptions 18 | get() = containingFile.grammarOptions 19 | 20 | 21 | } 22 | -------------------------------------------------------------------------------- /src/main/kotlin/com/github/oowekyala/ijcc/lang/psi/JccScopedExpansionUnit.kt: -------------------------------------------------------------------------------- 1 | // This is a generated file. Not intended for manual editing. 2 | package com.github.oowekyala.ijcc.lang.psi 3 | 4 | import com.github.oowekyala.ijcc.lang.psi.stubs.JccScopedExpansionUnitStub 5 | import com.intellij.psi.StubBasedPsiElement 6 | 7 | interface JccScopedExpansionUnit 8 | : JccIdentifierOwner, 9 | JccExpansionUnit, 10 | JjtNodeClassOwner, 11 | StubBasedPsiElement { 12 | 13 | val expansionUnit: JccExpansionUnit 14 | 15 | override val jjtreeNodeDescriptor: JccJjtreeNodeDescriptor 16 | 17 | override fun getNameIdentifier(): JccIdentifier? 18 | 19 | } 20 | -------------------------------------------------------------------------------- /src/main/kotlin/com/github/oowekyala/ijcc/util/EnclosedLogger.kt: -------------------------------------------------------------------------------- 1 | package com.github.oowekyala.ijcc.util 2 | 3 | import com.intellij.openapi.diagnostic.Logger 4 | 5 | /** 6 | * Base class for a hidden logger object enclosed in the relevant class. 7 | * Usage: 8 | * 9 | * private object Log : EnclosedLogger() 10 | * // ... 11 | * { 12 | * Log { debug("bla bla") } 13 | * } 14 | */ 15 | abstract class EnclosedLogger { 16 | 17 | private val logger = javaClass.enclosingClass 18 | .let { it?.name ?: javaClass.name } 19 | .let { Logger.getInstance(it) } 20 | 21 | operator fun invoke(block: Logger.() -> Unit) = logger.block() 22 | 23 | } -------------------------------------------------------------------------------- /src/main/kotlin/com/github/oowekyala/ijcc/lang/psi/JccJjtreeNodeDescriptorExpr.kt: -------------------------------------------------------------------------------- 1 | package com.github.oowekyala.ijcc.lang.psi 2 | 3 | import com.github.oowekyala.ijcc.lang.JccTypes 4 | 5 | /** 6 | * Expression in a [JccJjtreeNodeDescriptor]. 7 | * 8 | * @author Clément Fournier 9 | * @since 1.0 10 | */ 11 | interface JccJjtreeNodeDescriptorExpr : JccPsiElement { 12 | 13 | val isGtExpression: Boolean 14 | // the first child is the parenthesis 15 | get() = firstChild.nextSiblingNoWhitespace?.node?.elementType === JccTypes.JCC_GT 16 | 17 | val javaExpression: JccJavaExpression 18 | } 19 | 20 | val JccJjtreeNodeDescriptorExpr.expressionText: String 21 | get() = javaExpression.text 22 | -------------------------------------------------------------------------------- /src/main/kotlin/com/github/oowekyala/ijcc/lang/psi/impl/GrammarOptionsService.kt: -------------------------------------------------------------------------------- 1 | package com.github.oowekyala.ijcc.lang.psi.impl 2 | 3 | import com.github.oowekyala.ijcc.lang.model.IGrammarOptions 4 | import com.github.oowekyala.ijcc.lang.model.InlineGrammarOptions 5 | import com.intellij.openapi.project.Project 6 | 7 | /** 8 | * @author Clément Fournier 9 | */ 10 | open class GrammarOptionsService { 11 | 12 | 13 | open fun buildOptions(jccFileImpl: JccFileImpl): IGrammarOptions = 14 | InlineGrammarOptions(jccFileImpl) 15 | 16 | } 17 | 18 | val Project.grammarOptionsService: GrammarOptionsService 19 | get() = getComponent(GrammarOptionsService::class.java) ?: GrammarOptionsService() // default 20 | -------------------------------------------------------------------------------- /src/test/kotlin/com/github/oowekyala/ijcc/lang/parser/Jcc21ParserTests.kt: -------------------------------------------------------------------------------- 1 | package com.github.oowekyala.ijcc.lang.parser 2 | 3 | import com.github.oowekyala.ijcc.CongoccParserDefinition 4 | import com.github.oowekyala.ijcc.lang.ParserTestDataPath 5 | import com.intellij.testFramework.ParsingTestCase 6 | 7 | /** 8 | * @author Clément Fournier 9 | * @since 1.6 10 | */ 11 | class Jcc21ParserTests : ParsingTestCase("21", "ccc", CongoccParserDefinition()) { 12 | 13 | private val checkIt = true 14 | 15 | // TODO the end is wrong 16 | fun testSimple() = doTest(checkIt) 17 | 18 | override fun getTestDataPath(): String = ParserTestDataPath 19 | 20 | override fun skipSpaces(): Boolean = true 21 | } 22 | -------------------------------------------------------------------------------- /src/main/kotlin/com/github/oowekyala/ijcc/ide/findusages/JccTextSelectioner.kt: -------------------------------------------------------------------------------- 1 | package com.github.oowekyala.ijcc.ide.findusages 2 | 3 | import com.github.oowekyala.ijcc.lang.psi.JccIdentifier 4 | import com.github.oowekyala.ijcc.lang.psi.owner 5 | import com.intellij.codeInsight.hint.ImplementationTextSelectioner 6 | import com.intellij.psi.PsiElement 7 | 8 | 9 | class JccTextSelectioner : ImplementationTextSelectioner { 10 | override fun getTextEndOffset(elt: PsiElement): Int = 11 | (elt as? JccIdentifier)?.owner?.textRange?.endOffset 12 | ?: elt.textRange.endOffset 13 | 14 | 15 | override fun getTextStartOffset(element: PsiElement): Int = 16 | element.textRange.startOffset 17 | 18 | } 19 | -------------------------------------------------------------------------------- /src/main/kotlin/com/github/oowekyala/ijcc/lang/psi/RegexLikeDFVisitor.kt: -------------------------------------------------------------------------------- 1 | package com.github.oowekyala.ijcc.lang.psi 2 | 3 | /** 4 | * @author Clément Fournier 5 | * @since 1.0 6 | */ 7 | abstract class RegexLikeDFVisitor : DepthFirstVisitor() { 8 | 9 | 10 | abstract override fun visitLiteralRegularExpression(o: JccLiteralRegularExpression) 11 | 12 | abstract override fun visitNamedRegularExpression(o: JccNamedRegularExpression) 13 | 14 | abstract override fun visitEofRegularExpression(o: JccEofRegularExpression) 15 | 16 | abstract override fun visitRefRegularExpression(o: JccRefRegularExpression) 17 | 18 | abstract override fun visitContainerRegularExpression(o: JccContainerRegularExpression) 19 | 20 | } -------------------------------------------------------------------------------- /src/main/kotlin/com/github/oowekyala/ijcc/lang/psi/stubs/indices/IdeStubService.kt: -------------------------------------------------------------------------------- 1 | package com.github.oowekyala.ijcc.lang.psi.stubs.indices 2 | 3 | import com.github.oowekyala.ijcc.lang.psi.stubs.JjtNodeClassOwnerStub 4 | import com.github.oowekyala.ijcc.lang.psi.stubs.StubIndexService 5 | import com.github.oowekyala.ijcc.util.runIt 6 | import com.intellij.psi.stubs.IndexSink 7 | 8 | /** 9 | * @author Clément Fournier 10 | */ 11 | class IdeStubService : StubIndexService() { 12 | 13 | override fun indexJjtreeNodeClassOwner(stub: JjtNodeClassOwnerStub<*>, sink: IndexSink) { 14 | stub.jjtNodeQualifiedName?.runIt { 15 | sink.occurrence(JjtreeQNameStubIndex.key, it) 16 | } 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /src/main/kotlin/com/github/oowekyala/ijcc/ide/completion/LookupUtil.kt: -------------------------------------------------------------------------------- 1 | package com.github.oowekyala.ijcc.ide.completion 2 | 3 | import com.intellij.codeInsight.TailType 4 | import com.intellij.codeInsight.completion.PrioritizedLookupElement 5 | import com.intellij.codeInsight.lookup.LookupElement 6 | import com.intellij.codeInsight.lookup.TailTypeDecorator 7 | 8 | 9 | fun LookupElement.withTail(tail: String): LookupElement = 10 | TailTypeDecorator.withTail(this, MultiCharTailType(tail)) 11 | 12 | fun LookupElement.withTail(tailType: TailType): LookupElement = 13 | TailTypeDecorator.withTail(this, tailType) 14 | 15 | fun LookupElement.withPriority(priority: Double): LookupElement = 16 | PrioritizedLookupElement.withPriority(this, priority) -------------------------------------------------------------------------------- /src/test/kotlin/com/github/oowekyala/ijcc/lang/JccFoldingBuilderTest.kt: -------------------------------------------------------------------------------- 1 | package com.github.oowekyala.ijcc.lang 2 | 3 | import com.github.oowekyala.ijcc.lang.util.JccTestBase 4 | 5 | /** 6 | * @author Clément Fournier 7 | * @since 1.0 8 | */ 9 | class JccFoldingBuilderTest : JccTestBase() { 10 | 11 | 12 | override fun getTestDataPath(): String = TestResourcesPath 13 | 14 | fun testFolding() { 15 | myFixture.configureByFiles() 16 | myFixture.testFoldingWithCollapseStatus("$FoldingTestDataPath/ParserActions.jjt") 17 | } 18 | 19 | fun testFoldingJjtreeGen() { 20 | myFixture.configureByFiles() 21 | myFixture.testFoldingWithCollapseStatus("$FoldingTestDataPath/JjtreeGen.jj") 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /src/main/kotlin/com/github/oowekyala/ijcc/lang/psi/JccTypesExt.kt: -------------------------------------------------------------------------------- 1 | package com.github.oowekyala.ijcc.lang.psi 2 | 3 | import com.github.oowekyala.ijcc.lang.JccTypes 4 | import com.github.oowekyala.ijcc.lang.JccTypes.* 5 | import com.intellij.psi.TokenType 6 | import com.intellij.psi.tree.TokenSet 7 | 8 | /** 9 | * @author Clément Fournier 10 | * @since 1.0 11 | */ 12 | object JccTypesExt : JccTypes { 13 | 14 | val IdentifierTypeSet = TokenSet.create(JCC_IDENT) 15 | 16 | val CommentTypeSet = TokenSet.create( 17 | JCC_END_OF_LINE_COMMENT, 18 | JCC_C_STYLE_COMMENT 19 | ) 20 | 21 | val StringLiteralTypeSet = TokenSet.create(JCC_STRING_LITERAL) 22 | 23 | val WhitespaceTypeSet = TokenSet.create(TokenType.WHITE_SPACE) 24 | 25 | } 26 | -------------------------------------------------------------------------------- /src/main/kotlin/com/github/oowekyala/ijcc/lang/psi/stubs/DataStreamUtils.kt: -------------------------------------------------------------------------------- 1 | package com.github.oowekyala.ijcc.lang.psi.stubs 2 | 3 | import com.intellij.util.io.DataInputOutputUtil 4 | import java.io.DataInputStream 5 | import java.io.DataOutputStream 6 | 7 | inline fun > 8 | DataInputStream.readEnum(): T = T::class.java.enumConstants[readInt()] 9 | 10 | fun > DataOutputStream.writeEnum(t: T) = writeInt(t.ordinal) 11 | 12 | fun DataOutputStream.writeNullable(t: T?, writer: DataOutputStream.(T) -> Unit): Unit = 13 | DataInputOutputUtil.writeNullable(this, t) { writer(this, it) } 14 | 15 | fun DataInputStream.readNullable(reader: DataInputStream.() -> T): T? = 16 | DataInputOutputUtil.readNullable(this) { reader(this) } 17 | -------------------------------------------------------------------------------- /src/main/kotlin/com/github/oowekyala/ijcc/lang/model/JccVersion.kt: -------------------------------------------------------------------------------- 1 | package com.github.oowekyala.ijcc.lang.model 2 | 3 | /** 4 | * Version of Javacc, based on the versions published to maven. 5 | * 6 | * @author Clément Fournier 7 | * @since 1.0 8 | */ 9 | enum class JccVersion { 10 | V_2_1, // 11 | V_3_2, // Mar, 2006 12 | V_4_0, // Mar, 2006 13 | V_4_1, // Oct, 2008 14 | V_4_2, // Feb, 2009 15 | V_5_0, // Sep, 2009 16 | V_6_1_0, // Apr, 2014 17 | V_6_1_1, // May, 2014 18 | V_6_1_2, // May, 2014 19 | V_7_0_0, // Dec, 2016 20 | V_7_0_1, // Jan, 2017 21 | V_7_0_2, // Jan, 2017 22 | V_7_0_3, // Nov, 2017 23 | V_7_0_4; // Sep, 2018 24 | 25 | companion object { 26 | val Latest = values().last() 27 | } 28 | } -------------------------------------------------------------------------------- /src/main/resources/com/github/oowekyala/ijcc/icons/jccNavigateToNode.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/main/resources/com/github/oowekyala/ijcc/icons/jccNavigateToNode_dark.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/main/kotlin/com/github/oowekyala/ijcc/icons/JccCoreIcons.kt: -------------------------------------------------------------------------------- 1 | package com.github.oowekyala.ijcc.icons 2 | 3 | import com.intellij.openapi.util.IconLoader 4 | import javax.swing.Icon 5 | 6 | /** 7 | * These icons should *not* depend on icons.jar, ie 8 | * not use AllIcons and only use icons shipped in this jar. 9 | */ 10 | enum class JccCoreIcons(icon: Icon) : Icon by icon { 11 | 12 | /** File type icon. */ 13 | JAVACC_FILE("jccFile.svg"), 14 | JJTREE_FILE("jjtreeFile.svg"), 15 | JJTRICKS_FILE("jjtreeFile.svg"), 16 | ; 17 | 18 | constructor(fname: String) : this(IconLoader.getIcon(fname, JccCoreIcons::class.java)) 19 | 20 | companion object { 21 | 22 | fun default(): Icon = IconLoader.getIcon("jjtreeNodeLocate.svg", JccCoreIcons::class.java) 23 | } 24 | } 25 | 26 | -------------------------------------------------------------------------------- /src/main/kotlin/com/github/oowekyala/ijcc/lang/psi/stubs/StubIndexService.kt: -------------------------------------------------------------------------------- 1 | package com.github.oowekyala.ijcc.lang.psi.stubs 2 | 3 | import com.intellij.openapi.application.ApplicationManager 4 | import com.intellij.psi.stubs.IndexSink 5 | 6 | /** 7 | * Overridable stub service, implemented by the plugin. 8 | * 9 | * @author Clément Fournier 10 | */ 11 | open class StubIndexService protected constructor() { 12 | 13 | open fun indexJjtreeNodeClassOwner(stub: JjtNodeClassOwnerStub<*>, sink: IndexSink) { 14 | } 15 | 16 | companion object { 17 | @JvmStatic 18 | fun getInstance(): StubIndexService = 19 | ApplicationManager.getApplication().getService(StubIndexService::class.java) ?: NO_INDEX 20 | 21 | private val NO_INDEX = StubIndexService() 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /src/main/kotlin/com/github/oowekyala/ijcc/lang/injection/InjectionUtil.kt: -------------------------------------------------------------------------------- 1 | package com.github.oowekyala.ijcc.lang.injection 2 | 3 | import com.github.oowekyala.ijcc.lang.psi.JccGrammarFileRoot 4 | import com.intellij.openapi.util.Key 5 | 6 | 7 | private fun getLinearStructureFor(grammarFileRoot: JccGrammarFileRoot): LinearInjectedStructure = 8 | TreeLineariserVisitor.linearise(InjectedTreeBuilderVisitor.getInjectedSubtreeFor(grammarFileRoot)) 9 | 10 | private val LinearStructureKey = Key.create("linearInjectedStructure") 11 | 12 | val JccGrammarFileRoot.linearInjectedStructure: LinearInjectedStructure 13 | get() = getUserData(LinearStructureKey) 14 | ?: getLinearStructureFor(this) 15 | .also { 16 | // putUserData(LinearStructureKey, it) 17 | } 18 | 19 | -------------------------------------------------------------------------------- /src/test/kotlin/com/github/oowekyala/ijcc/ide/highlight/JccRichHighlightTest.kt: -------------------------------------------------------------------------------- 1 | package com.github.oowekyala.ijcc.ide.highlight 2 | 3 | import com.github.oowekyala.ijcc.util.JccAnnotationTestBase 4 | import org.junit.Ignore 5 | 6 | /** 7 | * FIXME info annots are not checked! 8 | * 9 | * @author Clément Fournier 10 | * @since 1.0 11 | */ 12 | @Ignore 13 | class JccRichHighlightTest : JccAnnotationTestBase() { 14 | 15 | 16 | fun testNormalRegexLiteralReference() = checkByText( 17 | """ 18 | TOKEN: { 19 | 20 | } 21 | 22 | void Foo():{} { 23 | "foo" 24 | } 25 | """.inGrammarCtx(), 26 | checkInfo = true 27 | ) 28 | 29 | 30 | } -------------------------------------------------------------------------------- /src/main/kotlin/com/github/oowekyala/ijcc/ide/findusages/JccDescriptionProvider.kt: -------------------------------------------------------------------------------- 1 | package com.github.oowekyala.ijcc.ide.findusages 2 | 3 | import com.intellij.psi.ElementDescriptionLocation 4 | import com.intellij.psi.ElementDescriptionProvider 5 | import com.intellij.psi.PsiElement 6 | import com.intellij.usageView.UsageViewLongNameLocation 7 | 8 | import com.github.oowekyala.ijcc.lang.psi.JccPsiElement 9 | 10 | /** 11 | * Describes elements for the usage view. (TODO) 12 | * 13 | * @author Clément Fournier 14 | * @since 1.0 15 | */ 16 | class JccDescriptionProvider : ElementDescriptionProvider { 17 | override fun getElementDescription(element: PsiElement, location: ElementDescriptionLocation): String? = 18 | if (element is JccPsiElement && location is UsageViewLongNameLocation) element.text 19 | else null 20 | } 21 | -------------------------------------------------------------------------------- /src/main/kotlin/com/github/oowekyala/ijcc/util/SoftReferenceCache.kt: -------------------------------------------------------------------------------- 1 | package com.github.oowekyala.ijcc.util 2 | 3 | import java.lang.ref.Reference 4 | import java.lang.ref.SoftReference 5 | import kotlin.reflect.KProperty 6 | 7 | /** 8 | * Delegated property type. 9 | * 10 | * @author Clément Fournier 11 | * @since 1.0 12 | */ 13 | class SoftReferenceCache(private val supplier: () -> T) { 14 | 15 | private var ref: Reference? = null 16 | 17 | operator fun getValue(thisRef: Any?, property: KProperty<*>): T { 18 | val cached = ref?.get() 19 | return if (cached == null) { 20 | val result = supplier() 21 | ref = SoftReference(result) 22 | return result 23 | } else cached 24 | } 25 | 26 | } 27 | 28 | 29 | fun softCache(supplier: () -> T) = SoftReferenceCache(supplier) -------------------------------------------------------------------------------- /src/main/resources/com/github/oowekyala/ijcc/optionDescriptions/CHOICE_AMBIGUITY_CHECK.html: -------------------------------------------------------------------------------- 1 | This is the number of tokens considered in checking choices 2 | of the form A | B | ... for ambiguity. 3 | 4 |

For example, if there is a common two token prefix for both A 5 | and B, but no common three token prefix, (assume this option 6 | is set to 3) then JavaCC can tell you to use a lookahead of 3 for disambiguation 7 | purposes. And if A and B have a common three token prefix, then JavaCC only 8 | tell you that you need to have a lookahead of 3 or more. Increasing this can 9 | give you more comprehensive ambiguity information at the cost of more processing 10 | time. For large grammars such as the Java grammar, increasing this number any 11 | further causes the checking to take too much time. -------------------------------------------------------------------------------- /src/main/kotlin/com/github/oowekyala/ijcc/lang/psi/stubs/indices/JjtreeQNameStubIndex.kt: -------------------------------------------------------------------------------- 1 | package com.github.oowekyala.ijcc.lang.psi.stubs.indices 2 | 3 | import com.github.oowekyala.ijcc.lang.psi.JjtNodeClassOwner 4 | import com.intellij.psi.stubs.StringStubIndexExtension 5 | import com.intellij.psi.stubs.StubIndexKey 6 | 7 | /** 8 | * Indexes the node class owners of a grammar by qname, to provide 9 | * links from the node classes to the productions. 10 | * 11 | * @author Clément Fournier 12 | * @since 1.2 13 | */ 14 | object JjtreeQNameStubIndex : StringStubIndexExtension() { 15 | 16 | private val Key = StubIndexKey.createIndexKey("jjtree.qname.owner") 17 | 18 | override fun getKey(): StubIndexKey = Key 19 | 20 | override fun getVersion(): Int = 1 21 | 22 | 23 | } 24 | -------------------------------------------------------------------------------- /src/main/kotlin/com/github/oowekyala/ijcc/lang/injection/MultilineTextEscaper.kt: -------------------------------------------------------------------------------- 1 | package com.github.oowekyala.ijcc.lang.injection 2 | 3 | import com.intellij.openapi.util.TextRange 4 | import com.intellij.psi.LiteralTextEscaper 5 | import com.intellij.psi.PsiLanguageInjectionHost 6 | 7 | /** 8 | * @author Clément Fournier 9 | * @since 1.0 10 | */ 11 | class MultilineTextEscaper(t: T) : LiteralTextEscaper(t) { 12 | override fun isOneLine(): Boolean = false 13 | 14 | override fun decode(rangeInsideHost: TextRange, outChars: StringBuilder): Boolean { 15 | outChars.append(rangeInsideHost.substring(myHost.text)) 16 | return true 17 | } 18 | 19 | override fun getOffsetInHost(offsetInDecoded: Int, rangeInsideHost: TextRange): Int { 20 | return rangeInsideHost.startOffset + offsetInDecoded 21 | } 22 | } -------------------------------------------------------------------------------- /src/main/kotlin/com/github/oowekyala/ijcc/ide/completion/MultiCharTailType.kt: -------------------------------------------------------------------------------- 1 | package com.github.oowekyala.ijcc.ide.completion 2 | 3 | import com.intellij.codeInsight.TailType 4 | import com.intellij.openapi.editor.Editor 5 | 6 | /** 7 | * @author Clément Fournier 8 | * @since 1.1 9 | */ 10 | data class MultiCharTailType(private val tail: String) : TailType() { 11 | 12 | override fun processTail(editor: Editor, tailOffset: Int): Int { 13 | 14 | val document = editor.document 15 | 16 | var finalOffset = tailOffset 17 | for (char in tail) { 18 | 19 | finalOffset = when (char) { 20 | document.charsSequence[tailOffset] -> moveCaret(editor, finalOffset, 1) 21 | else -> insertChar(editor, finalOffset, char) 22 | } 23 | } 24 | 25 | return finalOffset 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /.idea/runConfigurations/Run_plugin_in_sandbox.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 16 | 18 | true 19 | 20 | 21 | -------------------------------------------------------------------------------- /src/test/kotlin/com/github/oowekyala/ijcc/lang/ParserTestDsl.kt: -------------------------------------------------------------------------------- 1 | package com.github.oowekyala.ijcc.lang 2 | 3 | import com.github.oowekyala.ijcc.lang.util.AssertionMatcher 4 | import com.github.oowekyala.ijcc.lang.util.JccTestBase 5 | import com.github.oowekyala.ijcc.lang.util.PsiSpec 6 | import com.github.oowekyala.ijcc.lang.util.matchPsi 7 | import com.intellij.psi.PsiElement 8 | import io.kotest.matchers.should 9 | 10 | /** 11 | * @author Clément Fournier 12 | * @since 1.0 13 | */ 14 | 15 | 16 | abstract class ParserTestDsl : JccTestBase() { 17 | 18 | 19 | protected inline fun matchExpansion(ignoreChildren: Boolean = false, 20 | noinline nodeSpec: PsiSpec): AssertionMatcher = 21 | { 22 | it.asExpansion() should matchPsi(ignoreChildren, nodeSpec) 23 | } 24 | } 25 | 26 | -------------------------------------------------------------------------------- /src/main/kotlin/com/github/oowekyala/ijcc/ide/refs/JccLexicalStateReference.kt: -------------------------------------------------------------------------------- 1 | package com.github.oowekyala.ijcc.ide.refs 2 | 3 | import com.github.oowekyala.ijcc.lang.model.LexicalState 4 | import com.github.oowekyala.ijcc.lang.psi.JccIdentifier 5 | import com.intellij.psi.PsiElement 6 | import com.intellij.psi.PsiReferenceBase 7 | 8 | /** 9 | * @author Clément Fournier 10 | * @since 1.2 11 | */ 12 | class JccLexicalStateReference(element: JccIdentifier) : PsiReferenceBase(element) { 13 | 14 | override fun resolve(): PsiElement? = resolveState()?.declarationIdent 15 | 16 | fun resolveState(): LexicalState? = 17 | element.containingFile 18 | .lexicalGrammar 19 | .getLexicalState(element.name) 20 | 21 | 22 | override fun getVariants(): Array = 23 | JccRefVariantService.getInstance(element.project).lexicalStateVariants(this) 24 | } 25 | -------------------------------------------------------------------------------- /src/main/kotlin/com/github/oowekyala/ijcc/lang/cfa/LastExpansion.kt: -------------------------------------------------------------------------------- 1 | package com.github.oowekyala.ijcc.lang.cfa 2 | 3 | import com.github.oowekyala.ijcc.lang.psi.JccExpansion 4 | import com.github.oowekyala.ijcc.lang.psi.JccExpansionSequence 5 | import com.intellij.psi.PsiElement 6 | 7 | /** 8 | * Given that [bound] is an ancestor of this expansion, 9 | * finds out if [this] is executed last when executing 10 | * the [bound]. This is used to find the last expansion 11 | * of a node scope. 12 | */ 13 | fun JccExpansion.isNextStep(bound: PsiElement): Boolean { 14 | val parent = parent 15 | 16 | return when { 17 | this == bound -> true 18 | parent is JccExpansionSequence -> parent.expansionUnitList.last() == this && parent.isNextStep(bound) 19 | parent is JccExpansion -> parent.isNextStep(bound) 20 | else -> parent == bound 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/kotlin/com/github/oowekyala/ijcc/lang/psi/JccJavaBlock.kt: -------------------------------------------------------------------------------- 1 | package com.github.oowekyala.ijcc.lang.psi 2 | 3 | import com.github.oowekyala.ijcc.lang.injection.HostSpec 4 | import com.github.oowekyala.ijcc.lang.injection.MultilineTextEscaper 5 | import com.github.oowekyala.ijcc.lang.psi.impl.jccEltFactory 6 | import com.intellij.psi.LiteralTextEscaper 7 | import com.intellij.psi.PsiLanguageInjectionHost 8 | 9 | interface JccJavaBlock : JccPsiElement, PsiLanguageInjectionHost { 10 | 11 | override fun isValidHost(): Boolean = true 12 | 13 | override fun updateText(text: String): PsiLanguageInjectionHost = 14 | this.replace(project.jccEltFactory.createJavaBlock(text)) 15 | .let { it as PsiLanguageInjectionHost } 16 | .also { HostSpec.replaceHost(this, it) } 17 | 18 | override fun createLiteralTextEscaper(): LiteralTextEscaper = 19 | MultilineTextEscaper(this) 20 | } 21 | -------------------------------------------------------------------------------- /src/main/kotlin/com/github/oowekyala/ijcc/ide/gutter/BaseTargetingLineMarkerProvider.kt: -------------------------------------------------------------------------------- 1 | package com.github.oowekyala.ijcc.ide.gutter 2 | 3 | import com.intellij.codeInsight.daemon.RelatedItemLineMarkerInfo 4 | import com.intellij.codeInsight.daemon.RelatedItemLineMarkerProvider 5 | import com.intellij.psi.PsiElement 6 | 7 | /** 8 | * @author Clément Fournier 9 | * @since 1.2 10 | */ 11 | abstract class BaseTargetingLineMarkerProvider(private val target: Class) 12 | : RelatedItemLineMarkerProvider() { 13 | 14 | 15 | final override fun collectNavigationMarkers(element: PsiElement, result: MutableCollection>) { 16 | 17 | element.takeIf { target.isInstance(it) } 18 | ?.let { target.cast(it) } 19 | ?.let { processElt(it) } 20 | ?.forEach { result.add(it) } 21 | } 22 | 23 | abstract fun processElt(elt: T): Sequence> 24 | 25 | } 26 | -------------------------------------------------------------------------------- /src/main/kotlin/com/github/oowekyala/ijcc/lang/psi/JccJavaExpression.kt: -------------------------------------------------------------------------------- 1 | package com.github.oowekyala.ijcc.lang.psi 2 | 3 | import com.github.oowekyala.ijcc.lang.injection.HostSpec 4 | import com.github.oowekyala.ijcc.lang.injection.MultilineTextEscaper 5 | import com.github.oowekyala.ijcc.lang.psi.impl.jccEltFactory 6 | import com.intellij.psi.LiteralTextEscaper 7 | import com.intellij.psi.PsiLanguageInjectionHost 8 | 9 | interface JccJavaExpression : JccPsiElement, PsiLanguageInjectionHost { 10 | 11 | 12 | override fun isValidHost(): Boolean = true 13 | 14 | override fun updateText(text: String): PsiLanguageInjectionHost = 15 | this.replace(project.jccEltFactory.createJavaExpression(text)) 16 | .let { it as PsiLanguageInjectionHost } 17 | .also { HostSpec.replaceHost(this, it) } 18 | 19 | 20 | override fun createLiteralTextEscaper(): LiteralTextEscaper = 21 | MultilineTextEscaper(this) 22 | } 23 | -------------------------------------------------------------------------------- /src/main/kotlin/com/github/oowekyala/ijcc/ide/highlight/JavaccSyntaxHighlighter.kt: -------------------------------------------------------------------------------- 1 | package com.github.oowekyala.ijcc.ide.highlight 2 | 3 | import com.github.oowekyala.ijcc.lang.lexer.JavaccLexerAdapter 4 | import com.intellij.lexer.Lexer 5 | import com.intellij.openapi.editor.colors.TextAttributesKey 6 | import com.intellij.openapi.fileTypes.SyntaxHighlighterBase 7 | import com.intellij.psi.tree.IElementType 8 | 9 | /** 10 | * Syntax highlighter. 11 | * 12 | * @author Clément Fournier 13 | * @since 1.0 14 | */ 15 | class JavaccSyntaxHighlighter : SyntaxHighlighterBase() { 16 | override fun getTokenHighlights(tokenType: IElementType?): Array { 17 | val highlight = 18 | JavaccHighlightingColors.getTokenHighlight(tokenType) 19 | return when (highlight) { 20 | null -> emptyArray() 21 | else -> arrayOf(highlight) 22 | } 23 | } 24 | 25 | 26 | override fun getHighlightingLexer(): Lexer = JavaccLexerAdapter() 27 | } 28 | 29 | -------------------------------------------------------------------------------- /src/main/kotlin/com/github/oowekyala/ijcc/lang/psi/JccJavaCompilationUnit.kt: -------------------------------------------------------------------------------- 1 | package com.github.oowekyala.ijcc.lang.psi 2 | 3 | import com.github.oowekyala.ijcc.lang.injection.MultilineTextEscaper 4 | import com.intellij.psi.ElementManipulators 5 | import com.intellij.psi.LiteralTextEscaper 6 | import com.intellij.psi.PsiLanguageInjectionHost 7 | 8 | interface JccJavaCompilationUnit : PsiLanguageInjectionHost, JccPsiElement { 9 | override fun updateText(text: String): PsiLanguageInjectionHost = 10 | this.also { 11 | ElementManipulators.handleContentChange(this, text) 12 | } 13 | // this.replace(project.jccEltFactory.createJcu(text)) 14 | // .let { it as PsiLanguageInjectionHost } 15 | // .also { HostSpec.replaceHost(this, it) } 16 | 17 | override fun createLiteralTextEscaper(): LiteralTextEscaper = 18 | MultilineTextEscaper(this) 19 | 20 | override fun isValidHost(): Boolean = true 21 | 22 | } 23 | -------------------------------------------------------------------------------- /src/main/kotlin/com/github/oowekyala/ijcc/lang/psi/JccJavaAssignmentLhs.kt: -------------------------------------------------------------------------------- 1 | package com.github.oowekyala.ijcc.lang.psi 2 | 3 | import com.github.oowekyala.ijcc.lang.injection.HostSpec 4 | import com.github.oowekyala.ijcc.lang.injection.MultilineTextEscaper 5 | import com.github.oowekyala.ijcc.lang.psi.impl.jccEltFactory 6 | import com.intellij.psi.LiteralTextEscaper 7 | import com.intellij.psi.PsiLanguageInjectionHost 8 | 9 | interface JccJavaAssignmentLhs : JccPsiElement, PsiLanguageInjectionHost { 10 | 11 | val javaName: JccJavaName 12 | 13 | override fun isValidHost(): Boolean = true 14 | 15 | override fun updateText(text: String): PsiLanguageInjectionHost = 16 | this.replace(project.jccEltFactory.createAssignmentLhs(text)) 17 | .let { it as PsiLanguageInjectionHost } 18 | .also { HostSpec.replaceHost(this, it) } 19 | 20 | 21 | override fun createLiteralTextEscaper(): LiteralTextEscaper = 22 | MultilineTextEscaper(this) 23 | } 24 | -------------------------------------------------------------------------------- /src/main/kotlin/com/github/oowekyala/ijcc/settings/JavaccProjectSettingsServiceImpl.kt: -------------------------------------------------------------------------------- 1 | package com.github.oowekyala.ijcc.settings 2 | 3 | import com.github.oowekyala.ijcc.settings.JavaccProjectSettingsService.JccSettingsState 4 | import com.intellij.openapi.components.PersistentStateComponent 5 | import com.intellij.openapi.components.State 6 | 7 | /** 8 | * @author Clément Fournier 9 | * @since 1.0 10 | */ 11 | @State(name = "JavaccProjectSettings") // use misc.xml 12 | class JavaccProjectSettingsServiceImpl : JavaccProjectSettingsService, 13 | PersistentStateComponent { 14 | 15 | override var myState = JccSettingsState() 16 | 17 | override fun getState(): JavaccProjectSettingsService.PersistableSettingsState = myState.toMutable() 18 | 19 | override fun loadState(state: JavaccProjectSettingsService.PersistableSettingsState) { 20 | myState = state.toImmutable() 21 | } 22 | 23 | // TODO notify bus when the injection level changes? 24 | } 25 | -------------------------------------------------------------------------------- /src/test/resources/com/github/oowekyala/ijcc/lang/parser/fixtures/JavaTypes.jjt: -------------------------------------------------------------------------------- 1 | PARSER_BEGIN(JJTreeParser) 2 | 3 | package org.javacc.jjtree; 4 | /** 5 | * This is my parser declaration 6 | */ 7 | public class JJTreeParser { 8 | 9 | } 10 | 11 | PARSER_END(JJTreeParser) 12 | 13 | 14 | 15 | void emptyAlt(List generic): {} 16 | { 17 | "f" 18 | } 19 | 20 | Frab emptyAlt2(List generic): {} 21 | { 22 | "f" 23 | } 24 | 25 | Frab emptyAlt9(List generic): {} 26 | { 27 | "f" 28 | } 29 | 30 | Frab emptyAlt3(List... generic): {} 31 | { 32 | "f" 33 | } 34 | 35 | Frab emptyAlt4(List... generic): {} 36 | { 37 | "f" 38 | } 39 | 40 | Frab[] emptyAlt5(List... generic): {} 41 | { 42 | "f" 43 | } 44 | 45 | Frab[] emptyAlt6(List[] generic): {} 46 | { 47 | "f" 48 | } 49 | 50 | Frab[] emptyAlt7(List[] generic): {} 51 | { 52 | "f" 53 | } 54 | Frab[] emptyAlt8(List[] generic): {} 55 | { 56 | "f" 57 | } 58 | -------------------------------------------------------------------------------- /src/test/resources/com/github/oowekyala/ijcc/lang/parser/fixtures/Lookaheads.jjt: -------------------------------------------------------------------------------- 1 | PARSER_BEGIN(JJTreeParser) 2 | 3 | package org.javacc.jjtree; 4 | /** 5 | * This is my parser declaration 6 | */ 7 | public class JJTreeParser { 8 | 9 | } 10 | 11 | PARSER_END(JJTreeParser) 12 | 13 | 14 | 15 | void lookaheads(): {} 16 | { 17 | LOOKAHEAD(1, ID(), {getToken(1).kind != NATURAL}) // lexical, syntactic, semantic 18 | LOOKAHEAD({getToken(1).kind != NATURAL}) // semantic, not syntactic 19 | LOOKAHEAD(1) // lexical 20 | LOOKAHEAD(TableAlias(), {getToken(1).kind != NATURAL}) // syntactic, semantic 21 | LOOKAHEAD(1, ("foo" | "bar") ) // lexical, syntactic 22 | 23 | LOOKAHEAD({ doSth(); }, {getToken(1).kind != NATURAL}) // invalid parse 24 | 25 | "f" | "fk" // FIXME shouldn't be affected 26 | } 27 | 28 | // shouldn't be affected by the failed lookahead above 29 | void foo(): {} { 30 | "a" | "b" 31 | } -------------------------------------------------------------------------------- /src/main/kotlin/com/github/oowekyala/ijcc/ide/refs/JccRefVariantService.kt: -------------------------------------------------------------------------------- 1 | package com.github.oowekyala.ijcc.ide.refs 2 | 3 | import com.intellij.openapi.project.Project 4 | 5 | /** 6 | * @author Clément Fournier 7 | */ 8 | open class JccRefVariantService { 9 | 10 | 11 | open fun nonterminalRefVariants(ref: JccNonTerminalReference): Array = emptyArray() 12 | open fun terminalVariants(ref: JccTerminalReference): Array = emptyArray() 13 | open fun lexicalStateVariants(ref: JccLexicalStateReference): Array = emptyArray() 14 | open fun stringLiteralVariants(ref: JccBnfStringLiteralReference): Array = emptyArray() 15 | open fun jjtreeNodeVariants(ref: JjtNodePolyReference): Array = emptyArray() 16 | 17 | 18 | companion object { 19 | @JvmStatic 20 | fun getInstance(project: Project): JccRefVariantService = 21 | project.getService(JccRefVariantService::class.java) ?: NO_VARIANTS 22 | 23 | private val NO_VARIANTS = JccRefVariantService() 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /src/main/kotlin/com/github/oowekyala/ijcc/lang/psi/LookaheadExtensions.kt: -------------------------------------------------------------------------------- 1 | package com.github.oowekyala.ijcc.lang.psi 2 | 3 | /* 4 | 5 | void lookaheads(): {} 6 | { 7 | LOOKAHEAD(1, ID(), {getToken(1).kind != NATURAL}) // lexical, syntactic, semantic 8 | LOOKAHEAD({getToken(1).kind != NATURAL}) // semantic, not syntactic 9 | LOOKAHEAD(1) // lexical 10 | LOOKAHEAD(TableAlias(), {getToken(1).kind != NATURAL}) // syntactic, semantic 11 | LOOKAHEAD(1, ("foo" | "bar") ) // lexical, syntactic 12 | 13 | LOOKAHEAD({ doSth(); }, {getToken(1).kind != NATURAL}) // invalid parse 14 | } 15 | */ 16 | val JccLocalLookaheadUnit.isLexical 17 | get() = integerLiteral != null 18 | 19 | val JccLocalLookaheadUnit.isSyntactic 20 | get() = expansion != null 21 | 22 | val JccLocalLookaheadUnit.isSemantic 23 | get() = javaExpression != null 24 | 25 | val JccLocalLookaheadUnit.lexicalAmount: Int? 26 | get() = integerLiteral?.text?.toInt() 27 | -------------------------------------------------------------------------------- /src/test/kotlin/com/github/oowekyala/ijcc/ide/inspections/ActionsWithinLookaheadInspectionTest.kt: -------------------------------------------------------------------------------- 1 | package com.github.oowekyala.ijcc.ide.inspections 2 | 3 | import com.github.oowekyala.ijcc.ide.inspections.ActionWithinLookaheadInspection.Companion.ProblemDescription 4 | 5 | /** 6 | * @author Clément Fournier 7 | * @since 1.0 8 | */ 9 | class ActionsWithinLookaheadInspectionTest : JccInspectionTestBase(ActionWithinLookaheadInspection()) { 10 | 11 | private fun ignored(s: String) = warningAnnot(s, ProblemDescription) 12 | 13 | fun testPos() = checkByText( 14 | """ 15 | LOOKAHEAD(4,Foo() ${ignored("{}")}) 16 | """.inExpansionCtx("Foo") 17 | ) 18 | 19 | fun testNeg() = checkByText( 20 | """ 21 | LOOKAHEAD(4,Foo() ${ignored("{}")}) {} 22 | """.inExpansionCtx("Foo") 23 | ) 24 | 25 | fun testNestedSyntactic() = checkByText( 26 | """ 27 | (LOOKAHEAD(1, "f" | LOOKAHEAD(4,Foo()${ignored("{}")}) Foo()) "foo")? 28 | """.inExpansionCtx("Foo") 29 | ) 30 | 31 | } -------------------------------------------------------------------------------- /src/main/kotlin/com/github/oowekyala/ijcc/lang/psi/impl/JccRegexExpansionUnitImpl.kt: -------------------------------------------------------------------------------- 1 | // This is a generated file. Not intended for manual editing. 2 | package com.github.oowekyala.ijcc.lang.psi.impl 3 | 4 | import com.github.oowekyala.ijcc.lang.psi.* 5 | import com.intellij.lang.ASTNode 6 | import com.intellij.psi.PsiElementVisitor 7 | 8 | class JccRegexExpansionUnitImpl(node: ASTNode) : JccAssignableExpansionUnitImpl(node), JccRegexExpansionUnit { 9 | 10 | override fun accept(visitor: JccVisitor) { 11 | visitor.visitRegexExpansionUnit(this) 12 | } 13 | 14 | override fun accept(visitor: PsiElementVisitor) { 15 | if (visitor is JccVisitor) 16 | accept(visitor) 17 | else 18 | super.accept(visitor) 19 | } 20 | 21 | override fun getRegularExpression(): JccRegularExpression = 22 | findNotNullChildByClass(JccRegularExpression::class.java) 23 | 24 | override fun getNameIdentifier(): JccIdentifier? = (regularExpression as? JccNamedRegularExpression)?.nameIdentifier 25 | 26 | } 27 | -------------------------------------------------------------------------------- /src/main/kotlin/com/github/oowekyala/ijcc/lang/psi/JccIdentifier.kt: -------------------------------------------------------------------------------- 1 | package com.github.oowekyala.ijcc.lang.psi 2 | 3 | import com.intellij.psi.PsiElement 4 | import com.intellij.psi.PsiNamedElement 5 | 6 | interface JccIdentifier : JccPsiElement, PsiNamedElement { 7 | 8 | override fun getName(): String 9 | 10 | val leaf: PsiElement 11 | } 12 | 13 | val JccIdentifier.owner: JccIdentifierOwner? 14 | get() = 15 | ancestors(includeSelf = false) 16 | .takeWhile { it is JccIdentifierOwner } 17 | .filterIsInstance() 18 | .lastOrNull { it.nameIdentifier == this } 19 | 20 | 21 | val JccIdentifier.isJjtreeNodeIdentifier: Boolean 22 | get() = parent is JccJjtreeNodeDescriptor 23 | || (parent as? JccJavaNonTerminalProductionHeader) 24 | ?.let { it.parent as JccNonTerminalProduction } 25 | ?.let { it.nameIdentifier == it.nodeIdentifier } == true 26 | 27 | val JccIdentifier.isLexicalStateName: Boolean 28 | get() = parent is JccLexicalStateList || parent is JccRegexSpec -------------------------------------------------------------------------------- /src/main/kotlin/com/github/oowekyala/ijcc/ide/rename/JccNamesValidator.kt: -------------------------------------------------------------------------------- 1 | package com.github.oowekyala.ijcc.ide.rename 2 | 3 | import com.intellij.lang.refactoring.JavaNamesValidator 4 | import com.intellij.openapi.project.Project 5 | 6 | /** 7 | * Validates names for the rename refactoring. 8 | * 9 | * @author Clément Fournier 10 | * @since 1.0 11 | */ 12 | class JccNamesValidator : JavaNamesValidator() { 13 | private val jccKeywords = listOf( 14 | "LOOKAHEAD", 15 | "IGNORE_CASE", 16 | "PARSER_BEGIN", 17 | "PARSER_END", 18 | "JAVACODE", 19 | "TOKEN", 20 | "SPECIAL_TOKEN", 21 | "MORE", 22 | "SKIP", 23 | "TOKEN_MGR_DECLS", 24 | "EOF" 25 | ) 26 | 27 | override fun isKeyword(name: String, project: Project?): Boolean = 28 | jccKeywords.contains(name) || super.isKeyword(name, project) 29 | 30 | override fun isIdentifier(name: String, project: Project?): Boolean = 31 | !jccKeywords.contains(name) && super.isIdentifier(name, project) 32 | 33 | } 34 | -------------------------------------------------------------------------------- /src/main/kotlin/com/github/oowekyala/ijcc/lang/psi/manipulators/JccIdentifierManipulator.kt: -------------------------------------------------------------------------------- 1 | package com.github.oowekyala.ijcc.lang.psi.manipulators 2 | 3 | import com.github.oowekyala.ijcc.lang.psi.JccIdentifier 4 | import com.github.oowekyala.ijcc.lang.psi.impl.jccEltFactory 5 | import com.intellij.openapi.util.TextRange 6 | import com.intellij.psi.AbstractElementManipulator 7 | import com.intellij.util.IncorrectOperationException 8 | 9 | 10 | /** 11 | * @author Clément Fournier 12 | * @since 1.0 13 | */ 14 | class JccIdentifierManipulator : AbstractElementManipulator() { 15 | @Throws(IncorrectOperationException::class) 16 | override fun handleContentChange(identifier: JccIdentifier, range: TextRange, newContent: String): JccIdentifier { 17 | val oldText = identifier.text 18 | val newText = oldText.substring(0, range.startOffset) + newContent + 19 | oldText.substring(range.endOffset) 20 | return identifier.replace(identifier.project.jccEltFactory.createIdentifier(newText)) as JccIdentifier 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/kotlin/com/github/oowekyala/ijcc/lang/psi/PrettyPrintingExtensions.kt: -------------------------------------------------------------------------------- 1 | package com.github.oowekyala.ijcc.lang.psi 2 | 3 | /** 4 | * TODO 5 | * @author Clément Fournier 6 | * @since 1.0 7 | */ 8 | fun JccPsiElement.foldingName(): String = when (this) { 9 | is JccOptionalExpansionUnit -> "[...]" 10 | is JccParenthesizedExpansionUnit -> "(...)" + occurrenceIndicator.foldingName() 11 | is JccOptionSection -> "options {..}" 12 | is JccParserDeclaration -> "/PARSER DECLARATION/" 13 | is JccTokenManagerDecls -> "/TOKEN MANAGER DECLARATIONS/" 14 | is JccRegexProduction -> "${regexKind.text}: {..}" 15 | is JccLocalLookaheadUnit -> { 16 | if (isLexical && !isSyntactic && !isSemantic) { 17 | "LOOKAHEAD($lexicalAmount)" 18 | } else "LOOKAHEAD(_)" // use one char instead of .. for alignment 19 | } 20 | 21 | is JccParserActionsUnit -> "{..}" 22 | 23 | 24 | else -> text 25 | } 26 | 27 | 28 | fun JccOccurrenceIndicator?.foldingName() = this?.text ?: "" 29 | 30 | -------------------------------------------------------------------------------- /src/test/resources/com/github/oowekyala/ijcc/lang/folding/fixtures/ParserActions.jjt: -------------------------------------------------------------------------------- 1 | PARSER_BEGIN(JJTreeParser) 2 | PARSER_END(JJTreeParser) 3 | 4 | SKIP : { 5 | "(:" { commentNestingDepth++; } 6 | } 7 | 8 | 9 | void QuantifiedExpr(): 10 | {} 11 | { 12 | ( { jjtThis.setUniversallyQuantified(false); }) 13 | VarBindingList(false) 14 | ExprSingle() 15 | } 16 | 17 | JAVACODE 18 | void node_descriptor_expression() #NodeDescriptorExpression 19 | { 20 | Token tok; 21 | int nesting = 1; 22 | while (true) { 23 | tok = getToken(1); 24 | if (tok.kind == 0) { 25 | throw new ParseException(); 26 | } 27 | if (tok.kind == LPAREN) nesting++; 28 | if (tok.kind == RPAREN) { 29 | nesting--; 30 | if (nesting == 0) break; 31 | } 32 | tok = getNextToken(); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Clément Fournier 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/main/kotlin/com/github/oowekyala/ijcc/ide/structureview/JavaccStructureViewBuilderFactory.kt: -------------------------------------------------------------------------------- 1 | package com.github.oowekyala.ijcc.ide.structureview 2 | 3 | import com.github.oowekyala.ijcc.lang.psi.impl.JccFileImpl 4 | import com.intellij.ide.structureView.StructureViewBuilder 5 | import com.intellij.ide.structureView.StructureViewModel 6 | import com.intellij.ide.structureView.TreeBasedStructureViewBuilder 7 | import com.intellij.lang.PsiStructureViewFactory 8 | import com.intellij.openapi.editor.Editor 9 | import com.intellij.psi.PsiFile 10 | 11 | class JavaccStructureViewBuilderFactory : PsiStructureViewFactory { 12 | 13 | override fun getStructureViewBuilder(psiFile: PsiFile): StructureViewBuilder? { 14 | return when (psiFile) { 15 | !is JccFileImpl -> null 16 | else -> object : TreeBasedStructureViewBuilder() { 17 | 18 | override fun isRootNodeShown(): Boolean = false 19 | 20 | override fun createStructureViewModel(editor: Editor?): StructureViewModel = 21 | JavaccFileStructureViewModel(psiFile) 22 | } 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/kotlin/com/github/oowekyala/ijcc/lang/model/J21Option.kt: -------------------------------------------------------------------------------- 1 | package com.github.oowekyala.ijcc.lang.model 2 | 3 | import com.github.oowekyala.ijcc.lang.model.JccOptionType.BaseOptionType.* 4 | 5 | /** 6 | * @author Clément Fournier 7 | * @since 1.0 8 | */ 9 | @Suppress("ClassName", "unused") 10 | sealed class J21Option(type: JccOptionType, staticDefaultValue: T?) 11 | : GenericOption(type, staticDefaultValue, GrammarNature.JJTREE) { 12 | 13 | override val name: String = javaClass.simpleName 14 | 15 | object SPECIAL_TOKENS_ARE_NODES : J21Option(BOOLEAN, false) 16 | 17 | object DEFAULT_LEXICAL_STATE : J21Option(STRING, "DEFAULT") 18 | object TABS_TO_SPACE : J21Option(INTEGER, 4) 19 | 20 | /* 21 | SPECIAL_TOKENS_ARE_NODES; 22 | PARSER_PACKAGE=javagrammar; 23 | DEFAULT_LEXICAL_STATE=JAVA; 24 | PRESERVE_LINE_ENDINGS=false; 25 | TABS_TO_SPACES=8; 26 | */ 27 | 28 | companion object { 29 | val values = listOf( 30 | SPECIAL_TOKENS_ARE_NODES, 31 | DEFAULT_LEXICAL_STATE, 32 | TABS_TO_SPACE 33 | ) 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /src/test/kotlin/com/github/oowekyala/ijcc/ide/inspections/UnnamedRegexInspectionTest.kt: -------------------------------------------------------------------------------- 1 | package com.github.oowekyala.ijcc.ide.inspections 2 | 3 | /** 4 | * @author Clément Fournier 5 | * @since 1.0 6 | */ 7 | class UnnamedRegexInspectionTest : JccInspectionTestBase(UnnamedRegexInspection()) { 8 | 9 | 10 | private fun generic(s: String) = warningAnnot(s, UnnamedRegexInspection.GenericProblemDesc) 11 | private fun reference(s: String) = warningAnnot(s, UnnamedRegexInspection.FreeStandingReferenceProblemDesc) 12 | 13 | fun testLiteralString() = checkByText( 14 | """< "foo" >""".inExpansionCtx() 15 | ) 16 | 17 | fun testAll() = checkByText( 18 | """ 19 | $DummyHeader 20 | 21 | TOKEN: { 22 | < FOO: "foo" > 23 | | ${generic("< \" Foo \" >")} 24 | | ${reference("< FOO >")} 25 | } 26 | 27 | void Foo() :{}{ 28 | < > < "foobar" > 29 | } 30 | 31 | """.trimIndent() 32 | ) 33 | 34 | fun testParen() = checkByText( 35 | generic("""< ("foo") >""").inExpansionCtx() 36 | ) 37 | 38 | 39 | } -------------------------------------------------------------------------------- /src/main/kotlin/com/github/oowekyala/ijcc/ide/intentions/ReplaceOptionValueIntention.kt: -------------------------------------------------------------------------------- 1 | package com.github.oowekyala.ijcc.ide.intentions 2 | 3 | import com.github.oowekyala.ijcc.lang.psi.JccOptionValue 4 | import com.github.oowekyala.ijcc.lang.psi.impl.jccEltFactory 5 | import com.intellij.openapi.editor.Editor 6 | import com.intellij.openapi.project.Project 7 | import com.intellij.psi.PsiDocumentManager 8 | 9 | /** 10 | * @author Clément Fournier 11 | */ 12 | class ReplaceOptionValueIntention(private val replacement: String) : 13 | JccSelfTargetingEditorIntentionBase( 14 | JccOptionValue::class.java, 15 | "Replace value with $replacement" 16 | ) { 17 | 18 | override fun isApplicableTo(element: JccOptionValue): Boolean = true 19 | 20 | override fun run(project: Project, editor: Editor, element: JccOptionValue): () -> Unit { 21 | 22 | val elt = project.jccEltFactory.createOptionValue(replacement) 23 | 24 | return { 25 | element.replace(elt) 26 | PsiDocumentManager.getInstance(project).doPostponedOperationsAndUnblockDocument(editor.document) 27 | } 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /src/test/kotlin/com/github/oowekyala/ijcc/lang/JccNodeExtensionsTest.kt: -------------------------------------------------------------------------------- 1 | package com.github.oowekyala.ijcc.lang 2 | 3 | import com.github.oowekyala.ijcc.lang.model.RegexKind 4 | import com.github.oowekyala.ijcc.lang.psi.JccContainerRegularExpression 5 | import com.github.oowekyala.ijcc.lang.psi.impl.jccEltFactory 6 | import com.github.oowekyala.ijcc.lang.psi.isPrivate 7 | import com.github.oowekyala.ijcc.lang.psi.isUnclosed 8 | import io.kotest.matchers.shouldBe 9 | import org.junit.Test 10 | 11 | /** 12 | * @author Clément Fournier 13 | * @since 1.0 14 | */ 15 | class JccNodeExtensionsTest : ParserTestDsl() { 16 | 17 | 18 | fun `test JccRegexSpec isPrivate`() { 19 | 20 | val spec = project.jccEltFactory.createRegexSpec(RegexKind.TOKEN, "<#FOO: \"f\" >") 21 | 22 | spec.isPrivate shouldBe true 23 | 24 | } 25 | 26 | fun `test unclosed container regex`() { 27 | 28 | "<".asRegex().isUnclosed shouldBe true 29 | "<\"f\"".asRegex().isUnclosed shouldBe true 30 | "<\"f\">".asRegex().isUnclosed shouldBe false 31 | 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/resources/com/github/oowekyala/ijcc/icons/bnfProdAndNode.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/main/kotlin/com/github/oowekyala/ijcc/ide/highlight/InjectedJavaHighlightVisitor.kt: -------------------------------------------------------------------------------- 1 | package com.github.oowekyala.ijcc.ide.highlight 2 | 3 | import com.github.oowekyala.ijcc.lang.psi.JccPsiElement 4 | import com.github.oowekyala.ijcc.settings.InjectionSupportLevel 5 | import com.github.oowekyala.ijcc.settings.pluginSettings 6 | import com.intellij.codeInsight.daemon.impl.analysis.HighlightVisitorImpl 7 | import com.intellij.lang.injection.InjectedLanguageManager 8 | import com.intellij.psi.PsiFile 9 | 10 | /** 11 | * Highlighter for java code fragments. 12 | * 13 | * @author Clément Fournier 14 | * @since 1.0 15 | */ 16 | class InjectedJavaHighlightVisitor : HighlightVisitorImpl() { 17 | 18 | override fun suitableForFile(file: PsiFile): Boolean = 19 | InjectedLanguageManager.getInstance(file.project).let { manager -> 20 | manager.isInjectedFragment(file) && manager.getInjectionHost(file).let { host -> 21 | host is JccPsiElement && host.pluginSettings.injectionSupportLevel == InjectionSupportLevel.FULL 22 | } 23 | } 24 | 25 | override fun clone(): HighlightVisitorImpl = 26 | InjectedJavaHighlightVisitor() 27 | 28 | } 29 | -------------------------------------------------------------------------------- /src/main/kotlin/com/github/oowekyala/ijcc/settings/JavaccProjectSettingsConfigurable.kt: -------------------------------------------------------------------------------- 1 | package com.github.oowekyala.ijcc.settings 2 | 3 | import com.intellij.openapi.options.Configurable 4 | import com.intellij.openapi.project.Project 5 | import com.intellij.openapi.util.Disposer 6 | import javax.swing.JComponent 7 | 8 | /** 9 | * Manages the plugin settings page. 10 | * 11 | * @author Clément Fournier 12 | * @since 1.0 13 | */ 14 | class JavaccProjectSettingsConfigurable(project: Project) : Configurable, Configurable.NoScroll { 15 | 16 | 17 | private val javaccSettings = project.javaccSettings 18 | 19 | private val panelCache: PluginSettingsPage by lazy { 20 | PluginSettingsPage( 21 | javaccSettings.myState 22 | ) 23 | } 24 | 25 | override fun isModified(): Boolean = panelCache.toState() != javaccSettings.myState 26 | 27 | override fun getDisplayName(): String = "JavaCC" 28 | 29 | override fun apply() { 30 | javaccSettings.myState = panelCache.toState() 31 | } 32 | 33 | override fun disposeUIResources() = Disposer.dispose(panelCache) 34 | 35 | override fun createComponent(): JComponent? = panelCache.mainPanel 36 | 37 | } -------------------------------------------------------------------------------- /src/main/resources/com/github/oowekyala/ijcc/optionDescriptions/STATIC.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 14 | 15 | 16 | 17 | 22 | 23 |
JavaCC 5 | If true, all methods and class variables are specified as static in the 6 | generated parser and token manager. This allows only one parser 7 | object to be present, but it improves the performance of the 8 | parser. To perform multiple parses during one run of your Java 9 | program, you will have to call the ReInit() method to reinitialize 10 | your parser if it is static. If the parser is non-static, you 11 | may use the "new" operator to construct as many parsers as you 12 | wish. These can all be used simultaneously from different threads. 13 |
JJTree 18 | Generate code for a static parser. The default for this is true. 19 | This must be used consistently with the equivalent JavaCC options. 20 | The value of this option is emitted in the JavaCC source. 21 |
24 | -------------------------------------------------------------------------------- /src/main/kotlin/com/github/oowekyala/ijcc/JavaccLanguage.kt: -------------------------------------------------------------------------------- 1 | package com.github.oowekyala.ijcc 2 | 3 | import com.intellij.lang.Language 4 | 5 | /** 6 | * JavaCC language. JJTree uses the same language instead of a dialect, 7 | * splitting them would probably cause a lot of duplication atm. There's 8 | * two file types though, [JavaccFileType] and [JjtreeFileType]. 9 | * 10 | * @author Clément Fournier 11 | * @since 1.0 12 | */ 13 | class JavaccLanguage private constructor() : Language("JavaCC") { 14 | 15 | override fun isCaseSensitive(): Boolean = true 16 | 17 | companion object { 18 | val INSTANCE get() = Language.findInstance(JavaccLanguage::class.java) 19 | fun newInstance() = JavaccLanguage() 20 | val displayName get() = INSTANCE.displayName 21 | } 22 | } 23 | 24 | class CongoccLanguage private constructor() : Language(JavaccLanguage.INSTANCE, "CongoCC") { 25 | 26 | override fun isCaseSensitive(): Boolean = true 27 | 28 | companion object { 29 | val INSTANCE get() = Language.findInstance(CongoccLanguage::class.java) 30 | fun newInstance() = CongoccLanguage() 31 | val displayName get() = INSTANCE.displayName 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/test/kotlin/com/github/oowekyala/ijcc/lang/parser/JccParserTests.kt: -------------------------------------------------------------------------------- 1 | package com.github.oowekyala.ijcc.lang.parser 2 | 3 | import com.github.oowekyala.ijcc.JavaccParserDefinition 4 | import com.github.oowekyala.ijcc.lang.ParserTestDataPath 5 | import com.intellij.testFramework.ParsingTestCase 6 | 7 | /** 8 | * @author Clément Fournier 9 | * @since 1.0 10 | */ 11 | class JccParserTests : ParsingTestCase("", "jjt", JavaccParserDefinition()) { 12 | 13 | private val checkIt = true 14 | 15 | fun testProductions() = doTest(checkIt) 16 | fun testTokens() = doTest(checkIt) 17 | fun testParentheses() = doTest(checkIt) 18 | fun testLookaheads() = doTest(checkIt) 19 | fun testJavaTypes() = doTest(checkIt) 20 | fun testExpansionFails() = doTest(checkIt) 21 | fun testRegexPrecedence() = doTest(checkIt) 22 | fun testAssignments() = doTest(checkIt) 23 | fun testTokenFail() = doTest(checkIt) 24 | fun testProductionTolerance() = doTest(checkIt) 25 | fun testJjtreeStuff() = doTest(checkIt) 26 | fun testAnnotations() = doTest(checkIt) 27 | 28 | override fun getTestDataPath(): String = ParserTestDataPath 29 | 30 | override fun skipSpaces(): Boolean = true 31 | } 32 | -------------------------------------------------------------------------------- /src/main/kotlin/com/github/oowekyala/ijcc/ide/quickdoc/JjtNodeDocMaker.kt: -------------------------------------------------------------------------------- 1 | package com.github.oowekyala.ijcc.ide.quickdoc 2 | 3 | import com.github.oowekyala.ijcc.ide.quickdoc.JccDocUtil.buildQuickDoc 4 | import com.github.oowekyala.ijcc.ide.quickdoc.JccNonTerminalDocMaker.BnfSectionName 5 | import com.github.oowekyala.ijcc.lang.psi.JccScopedExpansionUnit 6 | import com.github.oowekyala.ijcc.lang.psi.nodeIdentifier 7 | 8 | /** 9 | * @author Clément Fournier 10 | * @since 1.0 11 | */ 12 | object JjtNodeDocMaker { 13 | 14 | fun makeDoc(scopedUnit: JccScopedExpansionUnit): String? = 15 | scopedUnit.nodeIdentifier?.let { id -> 16 | buildQuickDoc { 17 | buildDefinition { 18 | append("#${id.name}") // use the unprefixed name 19 | } 20 | sections { 21 | buildSection(BnfSectionName, sectionDelim = " ::=") { 22 | scopedUnit.expansionUnit.let { 23 | JccNonTerminalDocMaker.ExpansionMinifierVisitor(this).startOn(it) 24 | } 25 | } 26 | jjtreeSection(scopedUnit) 27 | 28 | } 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /src/test/kotlin/com/github/oowekyala/ijcc/ide/inspections/UnnecessaryParenthesesInspectionTest.kt: -------------------------------------------------------------------------------- 1 | package com.github.oowekyala.ijcc.ide.inspections 2 | 3 | /** 4 | * @author Clément Fournier 5 | * @since 1.0 6 | */ 7 | class UnnecessaryParenthesesInspectionTest : JccInspectionTestBase(JccUnnecessaryParenthesesInspection()) { 8 | 9 | 10 | private fun warnung(s: String) = warningAnnot(s, JccUnnecessaryParenthesesInspection.ProblemDescription) 11 | 12 | 13 | fun testReferenceString() = checkByText( 14 | """ 15 | $DummyHeader 16 | 17 | TOKEN: { 18 | < FOO: "foo" > 19 | } 20 | 21 | void Foo() :{}{ 22 | ${warnung("( )")} 23 | } 24 | 25 | """.trimIndent() 26 | ) 27 | 28 | fun testParen() = checkByText( 29 | """< ("foo") > """.inExpansionCtx() 30 | ) 31 | 32 | fun testScoped() = checkByText( 33 | """ 34 | $DummyHeader 35 | 36 | TOKEN: { 37 | < #FOO: "foo" > 38 | } 39 | 40 | void Foo():{} { 41 | ( "f" #Foo ) #Bar 42 | } 43 | 44 | """.trimIndent() 45 | ) 46 | 47 | 48 | 49 | 50 | } 51 | -------------------------------------------------------------------------------- /src/main/kotlin/com/github/oowekyala/ijcc/ide/intentions/RemoveNameFromRegexIntention.kt: -------------------------------------------------------------------------------- 1 | package com.github.oowekyala.ijcc.ide.intentions 2 | 3 | import com.github.oowekyala.ijcc.lang.psi.JccNamedRegularExpression 4 | import com.github.oowekyala.ijcc.lang.psi.JccRegexSpec 5 | import com.github.oowekyala.ijcc.lang.psi.promoteToRegex 6 | import com.github.oowekyala.ijcc.lang.psi.safeReplace 7 | import com.intellij.codeInsight.intention.LowPriorityAction 8 | import com.intellij.openapi.editor.Editor 9 | import com.intellij.openapi.project.Project 10 | 11 | /** 12 | * @author Clément Fournier 13 | * @since 1.0 14 | */ 15 | class RemoveNameFromRegexIntention 16 | : SelfTargetingOffsetIndependentIntention( 17 | JccNamedRegularExpression::class.java, 18 | "Remove name from regex (may change semantics)" 19 | ), LowPriorityAction { 20 | override fun applyTo(project: Project, editor: Editor?, element: JccNamedRegularExpression) { 21 | element.safeReplace(element.regexElement!!.promoteToRegex()) 22 | } 23 | 24 | override fun isApplicableTo(element: JccNamedRegularExpression): Boolean { 25 | 26 | 27 | return element.parent !is JccRegexSpec && element.regexElement != null 28 | } 29 | } -------------------------------------------------------------------------------- /src/main/kotlin/com/github/oowekyala/ijcc/lang/model/GrammarNature.kt: -------------------------------------------------------------------------------- 1 | package com.github.oowekyala.ijcc.lang.model 2 | 3 | /** 4 | * Type of preprocessor used by a grammar. JJTree is a superset 5 | * of JavaCC so we can implement both with the exact same PSI. 6 | * The [nature][com.github.oowekyala.ijcc.lang.psi.grammarNature] of 7 | * a file is determined by file extension. 8 | * 9 | * Grammar natures are comparable, their ordering is determined 10 | * by inclusion relation of the languages. 11 | * 12 | * @property displayName The display name, in lower case. JavaCC and JJTree 13 | * should always be capitalized. 14 | * 15 | * @author Clément Fournier 16 | * @since 1.2 17 | */ 18 | @Suppress("MemberVisibilityCanBePrivate") 19 | enum class GrammarNature(val displayName: String, 20 | val conventionalExtension: String) { 21 | JAVACC("JavaCC", "jj"), 22 | JJTREE("JJTree", "jjt"), 23 | JJTRICKS("JJTricks", "jjtx"), 24 | J21("JavaCC 21", "javacc"), 25 | 26 | /** 27 | * Special nature in which all features are enabled, 28 | * used in injection. Higher than all features. 29 | */ 30 | UNKNOWN("unknown", ""); 31 | 32 | val dotAndExtension = ".$conventionalExtension" 33 | } 34 | -------------------------------------------------------------------------------- /src/main/kotlin/com/github/oowekyala/ijcc/lang/psi/impl/JccOptionBindingImpl.kt: -------------------------------------------------------------------------------- 1 | package com.github.oowekyala.ijcc.lang.psi.impl 2 | 3 | import com.github.oowekyala.ijcc.lang.psi.JccIdentifier 4 | import com.github.oowekyala.ijcc.lang.psi.JccOptionBinding 5 | import com.github.oowekyala.ijcc.lang.psi.JccOptionValue 6 | import com.github.oowekyala.ijcc.lang.psi.JccVisitor 7 | import com.intellij.lang.ASTNode 8 | import com.intellij.psi.PsiElementVisitor 9 | 10 | /** 11 | * This impl is needed to make getName() return non-null 12 | */ 13 | class JccOptionBindingImpl(node: ASTNode) : JccPsiElementImpl(node), JccOptionBinding { 14 | 15 | override val optionValue: JccOptionValue? 16 | get() = findChildByClass(JccOptionValue::class.java) 17 | 18 | 19 | fun accept(visitor: JccVisitor) { 20 | visitor.visitOptionBinding(this) 21 | } 22 | 23 | override fun accept(visitor: PsiElementVisitor) { 24 | if (visitor is JccVisitor) 25 | accept(visitor) 26 | else 27 | super.accept(visitor) 28 | } 29 | 30 | override fun getNameIdentifier(): JccIdentifier? { 31 | return findChildByClass(JccIdentifier::class.java) 32 | } 33 | 34 | override fun getName(): String = node.firstChildNode.text 35 | } 36 | -------------------------------------------------------------------------------- /src/main/kotlin/com/github/oowekyala/ijcc/lang/psi/manipulators/JccLiteralRegexManipulator.kt: -------------------------------------------------------------------------------- 1 | package com.github.oowekyala.ijcc.lang.psi.manipulators 2 | 3 | import com.github.oowekyala.ijcc.lang.psi.JccLiteralRegexUnit 4 | import com.github.oowekyala.ijcc.lang.psi.impl.jccEltFactory 5 | import com.intellij.openapi.util.TextRange 6 | import com.intellij.psi.AbstractElementManipulator 7 | import com.intellij.util.IncorrectOperationException 8 | 9 | /** 10 | * Manipulator for literal regexes. 11 | * 12 | * @author Clément Fournier 13 | * @since 1.0 14 | */ 15 | class JccLiteralRegexManipulator : AbstractElementManipulator() { 16 | @Throws(IncorrectOperationException::class) 17 | override fun handleContentChange( 18 | regex: JccLiteralRegexUnit, 19 | range: TextRange, 20 | newContent: String 21 | ): JccLiteralRegexUnit? { 22 | val oldText = regex.stringLiteral.text 23 | val newText = oldText.substring(0, range.startOffset) + newContent + 24 | oldText.substring(range.endOffset) 25 | return regex.replace( 26 | regex.project.jccEltFactory.createRegexElement( 27 | newText 28 | ) 29 | ) as JccLiteralRegexUnit? 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/kotlin/com/github/oowekyala/ijcc/ide/inspections/JccInspectionsProvider.kt: -------------------------------------------------------------------------------- 1 | package com.github.oowekyala.ijcc.ide.inspections 2 | 3 | import com.intellij.codeInspection.InspectionToolProvider 4 | 5 | /** 6 | * @author Clément Fournier 7 | * @since 1.0 8 | */ 9 | class JccInspectionsProvider : InspectionToolProvider { 10 | override fun getInspectionClasses(): Array> = arrayOf( 11 | TokenCanNeverBeMatchedInspection::class.java, 12 | BnfStringCanNeverBeMatchedInspection::class.java, 13 | UnnamedRegexInspection::class.java, 14 | UnnecessaryAngledBracesRegexInspection::class.java, 15 | JccUnnecessaryParenthesesInspection::class.java, 16 | SuspiciousNodeDescriptorExprInspection::class.java, 17 | UnusedProductionInspection::class.java, 18 | UnusedPrivateRegexInspection::class.java, 19 | EmptyParserActionsInspection::class.java, 20 | ConsecutiveParserActionsInspection::class.java, 21 | LookaheadIsNotAtChoicePointInspection::class.java, 22 | ActionWithinLookaheadInspection::class.java, 23 | LeftRecursiveProductionInspection::class.java, 24 | LoopInRegexInspection::class.java, 25 | RegexMayMatchEmptyStringInspection::class.java 26 | ) 27 | } 28 | -------------------------------------------------------------------------------- /src/main/kotlin/com/github/oowekyala/ijcc/ide/intentions/DeleteExpansionIntention.kt: -------------------------------------------------------------------------------- 1 | package com.github.oowekyala.ijcc.ide.intentions 2 | 3 | import com.github.oowekyala.ijcc.lang.psi.JccBnfProduction 4 | import com.github.oowekyala.ijcc.lang.psi.JccExpansion 5 | import com.intellij.codeInsight.intention.LowPriorityAction 6 | import com.intellij.codeInspection.IntentionWrapper 7 | import com.intellij.codeInspection.LocalQuickFix 8 | import com.intellij.openapi.editor.Editor 9 | import com.intellij.openapi.project.Project 10 | import com.intellij.psi.PsiFile 11 | 12 | /** 13 | * Simply deletes an expansion. 14 | * 15 | * @author Clément Fournier 16 | * @since 1.1 17 | */ 18 | class DeleteExpansionIntention(val name: String = "Delete expansion") 19 | : SelfTargetingOffsetIndependentIntention(JccExpansion::class.java, name), LowPriorityAction { 20 | 21 | override fun applyTo(project: Project, editor: Editor?, element: JccExpansion) = element.delete() 22 | 23 | override fun isApplicableTo(element: JccExpansion): Boolean = element.parent !is JccBnfProduction 24 | 25 | companion object { 26 | fun quickFix(name: String = "Delete expansion", file: PsiFile): LocalQuickFix = 27 | IntentionWrapper.wrapToQuickFix(DeleteExpansionIntention(name), file) 28 | } 29 | } 30 | 31 | -------------------------------------------------------------------------------- /src/main/kotlin/com/github/oowekyala/ijcc/lang/psi/JccJavaNonTerminalProductionHeader.kt: -------------------------------------------------------------------------------- 1 | package com.github.oowekyala.ijcc.lang.psi 2 | 3 | import com.github.oowekyala.ijcc.lang.injection.MultilineTextEscaper 4 | import com.github.oowekyala.ijcc.lang.psi.impl.jccEltFactory 5 | import com.intellij.psi.LiteralTextEscaper 6 | import com.intellij.psi.PsiLanguageInjectionHost 7 | 8 | /** 9 | * Header of a non-terminal production, containing the java parts. 10 | */ 11 | interface JccJavaNonTerminalProductionHeader : JccIdentifierOwner, PsiLanguageInjectionHost { 12 | 13 | val javaFormalParameterList: List 14 | 15 | val javaReturnType: JccJavaReturnType 16 | 17 | val javaThrowsList: JccJavaThrowsList? 18 | 19 | val javaAccessModifier: JccJavaAccessModifier 20 | 21 | override fun getNameIdentifier(): JccIdentifier 22 | 23 | fun toJavaMethodHeader(): String = text 24 | 25 | override fun isValidHost(): Boolean = false 26 | 27 | override fun createLiteralTextEscaper(): LiteralTextEscaper = 28 | MultilineTextEscaper(this) 29 | 30 | override fun updateText(text: String): PsiLanguageInjectionHost = 31 | this.replace(project.jccEltFactory.createJavaNonterminalHeader(text)) as PsiLanguageInjectionHost 32 | 33 | 34 | } 35 | -------------------------------------------------------------------------------- /src/main/kotlin/com/github/oowekyala/ijcc/ide/intentions/JccSelfTargetingEditorIntentionBase.kt: -------------------------------------------------------------------------------- 1 | package com.github.oowekyala.ijcc.ide.intentions 2 | 3 | import com.intellij.codeInsight.FileModificationService 4 | import com.intellij.openapi.application.ApplicationManager 5 | import com.intellij.openapi.editor.Editor 6 | import com.intellij.openapi.project.Project 7 | import com.intellij.psi.PsiElement 8 | 9 | /** 10 | * A [SelfTargetingOffsetIndependentIntention] that needs an editor to run. 11 | */ 12 | abstract class JccSelfTargetingEditorIntentionBase( 13 | target: Class, 14 | name: String 15 | ) : SelfTargetingOffsetIndependentIntention(target, name) { 16 | 17 | 18 | final override fun applyTo(project: Project, editor: Editor?, element: T) { 19 | if (editor == null) throw IllegalArgumentException("This intention requires an editor") 20 | 21 | val kRunnable = run(project, editor, element) 22 | 23 | FileModificationService.getInstance().prepareFileForWrite(element.containingFile) 24 | if (startInWriteAction()) { 25 | kRunnable() 26 | } else { 27 | ApplicationManager.getApplication().runWriteAction(kRunnable) 28 | } 29 | } 30 | 31 | 32 | protected abstract fun run(project: Project, editor: Editor, element: T): () -> Unit 33 | 34 | } -------------------------------------------------------------------------------- /src/main/kotlin/com/github/oowekyala/ijcc/ide/gutter/JccProductionToParserLineMarkerProvider.kt: -------------------------------------------------------------------------------- 1 | package com.github.oowekyala.ijcc.ide.gutter 2 | 3 | import com.github.oowekyala.ijcc.icons.JccIcons 4 | import com.github.oowekyala.ijcc.lang.psi.JccNonTerminalProduction 5 | import com.github.oowekyala.ijcc.lang.psi.parserMethod 6 | import com.intellij.codeInsight.daemon.RelatedItemLineMarkerInfo 7 | import com.intellij.codeInsight.navigation.NavigationGutterIconBuilder 8 | import com.intellij.psi.PsiElement 9 | 10 | /** 11 | * @author Clément Fournier 12 | * @since 1.3 13 | */ 14 | class JccProductionToParserLineMarkerProvider 15 | : BaseTargetingLineMarkerProvider(JccNonTerminalProduction::class.java) { 16 | 17 | override fun processElt(elt: JccNonTerminalProduction): Sequence> { 18 | 19 | val target = elt.parserMethod ?: return emptySequence() 20 | 21 | return NavigationGutterIconBuilder.create(JccIcons.GUTTER_PARSER_METHOD) 22 | .setTarget(target) 23 | .setTooltipText("Navigate to parser method in ${target.containingClass?.name}") 24 | .setPopupTitle("Parser class ${target.containingClass?.name}") 25 | .createLineMarkerInfo(elt.nameIdentifier.leaf) 26 | .let { sequenceOf(it) } 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /src/test/kotlin/com/github/oowekyala/ijcc/lang/TestUtil.kt: -------------------------------------------------------------------------------- 1 | package com.github.oowekyala.ijcc.lang 2 | 3 | import io.kotest.matchers.types.shouldBeInstanceOf 4 | import io.kotest.matchers.shouldBe 5 | 6 | 7 | const val TestResourcesPath = "src/test/resources/" 8 | const val PackagePath = "com/github/oowekyala/ijcc/" 9 | 10 | private const val Fixtures = "fixtures" 11 | const val FoldingTestDataPath = "$TestResourcesPath${PackagePath}lang/folding/$Fixtures" 12 | const val OptionsTestDataPath = "$TestResourcesPath${PackagePath}lang/options/$Fixtures" 13 | const val InjectionTestDataPath = "$TestResourcesPath${PackagePath}lang/injection/$Fixtures" 14 | const val ParserTestDataPath = "$TestResourcesPath${PackagePath}lang/parser/$Fixtures" 15 | 16 | 17 | inline fun Any?.shouldBeA(t: (T) -> Unit) { 18 | this.shouldBeInstanceOf() 19 | t(this as T) 20 | } 21 | 22 | fun Collection.shouldContainOneSuch(t: (T) -> Unit) { 23 | this.any { 24 | try { 25 | t(it) 26 | true 27 | } catch (ass: AssertionError) { 28 | false 29 | } 30 | } shouldBe true 31 | } 32 | 33 | //fun KClass<*>.dataPath(vararg addSegments: String) = 34 | // this.java.`package`.name 35 | // .replace('.', '/') 36 | // .let { "$it/${addSegments.joinToString(separator = "/")}" } 37 | -------------------------------------------------------------------------------- /src/test/kotlin/com/github/oowekyala/ijcc/ide/refs/ReferenceTest.kt: -------------------------------------------------------------------------------- 1 | package com.github.oowekyala.ijcc.ide.refs 2 | 3 | import com.github.oowekyala.ijcc.lang.psi.JjtNodeClassOwner 4 | import com.github.oowekyala.ijcc.lang.psi.descendantSequence 5 | import com.github.oowekyala.ijcc.lang.psi.typedReference 6 | import com.github.oowekyala.ijcc.lang.util.JccTestBase 7 | import io.kotest.matchers.collections.haveSize 8 | import io.kotest.matchers.should 9 | 10 | /** 11 | * @author Clément Fournier 12 | * @since 1.0 13 | */ 14 | class ReferenceTest : JccTestBase() { 15 | 16 | fun testJjtreePolyRef() { 17 | 18 | val file = """ 19 | 20 | $DummyHeader 21 | 22 | void Four():{} 23 | { 24 | "4" 25 | } 26 | 27 | void Foo():{} 28 | { 29 | Hello() "4" #Four 30 | } 31 | 32 | void Hello():{} 33 | { 34 | "4" #Four 35 | } 36 | 37 | 38 | 39 | void MyFour() #Four:{} 40 | { 41 | "4" 42 | } 43 | 44 | """.trimIndent().asJccFile() 45 | 46 | 47 | val owner = file.descendantSequence().filterIsInstance().first() 48 | 49 | owner.typedReference!!.multiResolve(false).toList() should haveSize(4) 50 | } 51 | 52 | 53 | } 54 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build & verify 2 | 3 | on: 4 | push: 5 | 6 | jobs: 7 | build: 8 | name: Build plugin, verify compatiblity against declared minimum support version of intellij. 9 | runs-on: ubuntu-latest 10 | steps: 11 | - name: Check out repository 12 | uses: actions/checkout@v2 13 | 14 | - name: Setup Java 11 15 | uses: actions/setup-java@v2 16 | with: 17 | distribution: zulu 18 | java-version: 11 19 | 20 | 21 | - name: Build the plugin using Gradle 22 | uses: eskatos/gradle-command-action@v1 23 | with: 24 | arguments: buildPlugin 25 | wrapper-cache-enabled: true 26 | dependencies-cache-enabled: true 27 | configuration-cache-enabled: true 28 | 29 | - name: Verify Plugin on IntelliJ Platforms 30 | id: verify 31 | uses: ChrisCarini/intellij-platform-plugin-verifier-action@latest 32 | with: 33 | ide-versions: | 34 | ideaIC:2021.1 35 | ideaIU:2021.1 36 | ideaIC:LATEST-EAP-SNAPSHOT 37 | 38 | - name: Get log file path and print contents 39 | run: | 40 | echo "The verifier log file [${{steps.verify.outputs.verification-output-log-filename}}] contents : " ; 41 | cat ${{steps.verify.outputs.verification-output-log-filename}} 42 | -------------------------------------------------------------------------------- /src/main/kotlin/com/github/oowekyala/ijcc/lang/psi/impl/JccRegexSpecImpl.kt: -------------------------------------------------------------------------------- 1 | // This is a generated file. Not intended for manual editing. 2 | package com.github.oowekyala.ijcc.lang.psi.impl 3 | 4 | import com.github.oowekyala.ijcc.lang.psi.* 5 | import com.intellij.lang.ASTNode 6 | import com.intellij.psi.PsiElementVisitor 7 | 8 | class JccRegexSpecImpl(node: ASTNode) : JccPsiElementImpl(node), JccRegexSpec { 9 | 10 | fun accept(visitor: JccVisitor) { 11 | visitor.visitRegexSpec(this) 12 | } 13 | 14 | override fun accept(visitor: PsiElementVisitor) { 15 | if (visitor is JccVisitor) 16 | accept(visitor) 17 | else 18 | super.accept(visitor) 19 | } 20 | 21 | override fun getRegularExpression(): JccRegularExpression = 22 | findNotNullChildByClass(JccRegularExpression::class.java) 23 | 24 | override fun getLexicalStateTransition(): JccIdentifier? = findChildByClass(JccIdentifier::class.java) 25 | 26 | override fun getLexicalActions(): JccJavaBlock? = findChildByClass(JccJavaBlock::class.java) 27 | 28 | override fun getJjtreeNodeDescriptor(): JccJjtreeNodeDescriptor? = findChildByClass(JccJjtreeNodeDescriptor::class.java) 29 | 30 | override fun getName(): String? = nameIdentifier?.text 31 | 32 | override fun getNameIdentifier(): JccIdentifier? = (regularExpression as? JccNamedRegularExpression)?.nameIdentifier 33 | } 34 | -------------------------------------------------------------------------------- /src/test/resources/com/github/oowekyala/ijcc/lang/parser/fixtures/Parentheses.jjt: -------------------------------------------------------------------------------- 1 | PARSER_BEGIN(JJTreeParser) 2 | 3 | package org.javacc.jjtree; 4 | /** 5 | * This is my parser declaration 6 | */ 7 | public class JJTreeParser { 8 | 9 | } 10 | 11 | PARSER_END(JJTreeParser) 12 | 13 | 14 | 15 | void parens() : 16 | {} 17 | { 18 | 19 | ("f")+ // necessary 20 | ("f")* // necessary 21 | ("f")? // necessary 22 | 23 | ("f") // unnecessary 24 | (foo()) // unnecessary 25 | (a=foo()) // unnecessary 26 | (a="f") // unnecessary 27 | (a=) // unnecessary 28 | () // unnecessary 29 | (try{""}catch(foo f){}) // unnecessary 30 | (["hello"]) // unnecessary 31 | (("")?) // unnecessary 32 | 33 | LOOKAHEAD( ("foo" | "bar") ) // unnecessary 34 | LOOKAHEAD(1, ("foo" | "bar") ) // unnecessary 35 | 36 | 37 | ("foo" | "bar") "bzaz" // necessary 38 | (("foo" | "bar") | "bzaz") // necessary, unnecessary 39 | 40 | ("foo" "bar") {} // unnecessary 41 | ("foo" "bar") (foo() | "f") // unnecessary, necessary 42 | } 43 | 44 | void foo():{}{ ("") } // unnecessary 45 | -------------------------------------------------------------------------------- /src/test/kotlin/com/github/oowekyala/ijcc/ide/inspections/EmptyParserActionsInspectionTest.kt: -------------------------------------------------------------------------------- 1 | package com.github.oowekyala.ijcc.ide.inspections 2 | 3 | /** 4 | * @author Clément Fournier 5 | * @since 1.1 6 | */ 7 | class EmptyParserActionsInspectionTest : JccInspectionTestBase(EmptyParserActionsInspection()) { 8 | 9 | private fun String.warn() = warningAnnot(this, EmptyParserActionsInspection.ProblemDescription) 10 | 11 | 12 | fun `test warning range`() = checkByText(""" 13 | $DummyHeader 14 | 15 | 16 | void Foo(): 17 | {} 18 | { 19 | "foo" "bar" ${"{ }".warn()} "flab" {weLiveInASociety();} 20 | } 21 | 22 | 23 | """) 24 | 25 | 26 | fun `test warning not suppressed`() = checkByText(""" 27 | $DummyHeader 28 | 29 | 30 | void Foo(): 31 | {} 32 | { 33 | "foo" "bar" ${"{ }".warn()} 34 | } 35 | 36 | 37 | """) 38 | 39 | 40 | fun `test warning void`() = checkByText(""" 41 | $DummyHeader 42 | 43 | 44 | void Foo() #void: 45 | {} 46 | { 47 | "foo" "bar" {goo();} ${"{ }".warn()} 48 | } 49 | 50 | 51 | """) 52 | 53 | fun `test warning suppressed 2`() = checkByText(""" 54 | $DummyHeader 55 | 56 | 57 | void Foo(): 58 | {} 59 | { 60 | ("foo" "bar" { jj(); } { }) #A 61 | } 62 | 63 | 64 | """) 65 | 66 | 67 | } 68 | -------------------------------------------------------------------------------- /src/main/kotlin/com/github/oowekyala/ijcc/lang/psi/JavaccAstFactory.kt: -------------------------------------------------------------------------------- 1 | package com.github.oowekyala.ijcc.lang.psi 2 | 3 | import com.intellij.lang.ASTFactory 4 | import com.intellij.lang.DefaultASTFactory 5 | import com.intellij.openapi.application.ApplicationManager 6 | import com.intellij.psi.impl.source.tree.CompositeElement 7 | import com.intellij.psi.impl.source.tree.FileElement 8 | import com.intellij.psi.impl.source.tree.LeafElement 9 | import com.intellij.psi.impl.source.tree.LeafPsiElement 10 | import com.intellij.psi.tree.IElementType 11 | import com.intellij.psi.tree.IFileElementType 12 | 13 | /** 14 | * Extension point. 15 | * 16 | * @author Clément Fournier 17 | * @since 1.0 18 | */ 19 | class JavaccAstFactory : ASTFactory() { 20 | 21 | override fun createComposite(type: IElementType): CompositeElement { 22 | return if (type is IFileElementType) { 23 | FileElement(type, null) 24 | } else CompositeElement(type) 25 | } 26 | 27 | override fun createLeaf(type: IElementType, text: CharSequence): LeafElement { 28 | return when { 29 | JccTypesExt.CommentTypeSet.contains(type) -> { 30 | val default = ApplicationManager.getApplication().getService(DefaultASTFactory::class.java) 31 | default.createComment(type, text) 32 | } 33 | 34 | else -> LeafPsiElement(type, text) 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/kotlin/com/github/oowekyala/ijcc/lang/psi/impl/JccIdentifierImpl.kt: -------------------------------------------------------------------------------- 1 | package com.github.oowekyala.ijcc.lang.psi.impl 2 | 3 | import com.github.oowekyala.ijcc.ide.refs.JccLexicalStateReference 4 | import com.github.oowekyala.ijcc.lang.JccTypes.JCC_IDENT 5 | import com.github.oowekyala.ijcc.lang.psi.JccIdentifier 6 | import com.github.oowekyala.ijcc.lang.psi.JccVisitor 7 | import com.github.oowekyala.ijcc.lang.psi.isLexicalStateName 8 | import com.intellij.lang.ASTNode 9 | import com.intellij.psi.PsiElement 10 | import com.intellij.psi.PsiElementVisitor 11 | import com.intellij.psi.PsiReference 12 | 13 | class JccIdentifierImpl(node: ASTNode) : JccPsiElementImpl(node), JccIdentifier { 14 | 15 | fun accept(visitor: JccVisitor) { 16 | visitor.visitIdentifier(this) 17 | } 18 | 19 | override val leaf: PsiElement 20 | get() = findNotNullChildByType(JCC_IDENT) 21 | 22 | override fun setName(name: String): PsiElement = replace(project.jccEltFactory.createIdentifier(name)) 23 | 24 | override fun getName(): String = text 25 | 26 | override fun getReference(): PsiReference? = when { 27 | isLexicalStateName -> JccLexicalStateReference(this) 28 | else -> null 29 | } 30 | 31 | override fun accept(visitor: PsiElementVisitor) { 32 | if (visitor is JccVisitor) { 33 | accept(visitor) 34 | } else { 35 | super.accept(visitor) 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/test/kotlin/com/github/oowekyala/ijcc/lang/psi/JccElementFactoryTest.kt: -------------------------------------------------------------------------------- 1 | package com.github.oowekyala.ijcc.lang.psi 2 | 3 | import com.github.oowekyala.ijcc.lang.psi.impl.jccEltFactory 4 | import com.github.oowekyala.ijcc.lang.util.JccTestBase 5 | import org.junit.Test 6 | 7 | /** 8 | * @author Clément Fournier 9 | * @since 1.0 10 | */ 11 | class JccElementFactoryTest : JccTestBase() { 12 | 13 | // mostly catches ClassCastExceptions 14 | 15 | 16 | fun testCreateOptionValue() { 17 | //TODO 18 | 19 | } 20 | 21 | 22 | fun testCreateRegexReference() { 23 | project.jccEltFactory.createRegexElement("") 24 | } 25 | 26 | 27 | fun testCreateLiteralRegexUnit() { 28 | project.jccEltFactory.createRegexElement("\"foo\"") 29 | } 30 | 31 | 32 | fun testCreateBnfExpansion() { 33 | // TODO 34 | } 35 | 36 | 37 | fun testCreateIdentifier() { 38 | project.jccEltFactory.createIdentifier("mlady") 39 | } 40 | 41 | 42 | 43 | fun testCreateJavaExpression() { 44 | project.jccEltFactory.createJavaExpression("1+2") 45 | } 46 | 47 | 48 | 49 | fun testCreateJavaBlock() { 50 | project.jccEltFactory.createJavaBlock("{ hey(); }") 51 | } 52 | 53 | 54 | 55 | fun testCreateAssignmentLhs() { 56 | project.jccEltFactory.createAssignmentLhs("foo.bar") 57 | } 58 | 59 | 60 | } 61 | -------------------------------------------------------------------------------- /src/main/kotlin/com/github/oowekyala/ijcc/lang/psi/manipulators/JccCommenter.kt: -------------------------------------------------------------------------------- 1 | package com.github.oowekyala.ijcc.lang.psi.manipulators 2 | 3 | import com.github.oowekyala.ijcc.lang.JccTypes 4 | import com.intellij.lang.CodeDocumentationAwareCommenterEx 5 | import com.intellij.psi.PsiComment 6 | import com.intellij.psi.PsiElement 7 | import com.intellij.psi.tree.IElementType 8 | 9 | class JccCommenter : CodeDocumentationAwareCommenterEx { 10 | override fun getLineCommentPrefix(): String? = "//" 11 | 12 | override fun getBlockCommentPrefix(): String? = "/*" 13 | 14 | override fun getBlockCommentSuffix(): String? = "*/" 15 | 16 | override fun getCommentedBlockCommentPrefix(): String? = null 17 | 18 | override fun getCommentedBlockCommentSuffix(): String? = null 19 | 20 | override fun getLineCommentTokenType(): IElementType? = JccTypes.JCC_END_OF_LINE_COMMENT 21 | 22 | override fun getBlockCommentTokenType(): IElementType? = JccTypes.JCC_C_STYLE_COMMENT 23 | 24 | // TODO javadoc: 25 | 26 | override fun getDocumentationCommentTokenType(): IElementType? = null 27 | 28 | override fun getDocumentationCommentPrefix(): String? = "/**" 29 | 30 | override fun getDocumentationCommentLinePrefix(): String? = "*" 31 | 32 | override fun getDocumentationCommentSuffix(): String? = "*/" 33 | 34 | override fun isDocumentationComment(element: PsiComment): Boolean = false 35 | 36 | override fun isDocumentationCommentText(element: PsiElement): Boolean = false 37 | } 38 | -------------------------------------------------------------------------------- /src/test/kotlin/com/github/oowekyala/ijcc/lang/psi/stubs/JccStubTest.kt: -------------------------------------------------------------------------------- 1 | package com.github.oowekyala.ijcc.lang.psi.stubs 2 | 3 | import com.github.oowekyala.ijcc.lang.util.JccTestBase 4 | import com.intellij.openapi.application.runWriteAction 5 | import com.intellij.openapi.vfs.VfsUtil 6 | import com.intellij.psi.impl.DebugUtil 7 | import com.intellij.psi.stubs.StubTreeLoader 8 | import org.intellij.lang.annotations.Language 9 | 10 | /** 11 | * @author Clément Fournier 12 | * @since 1.2 13 | */ 14 | class JccStubTest : JccTestBase() { 15 | 16 | 17 | fun `test non-terminals are stubbed under the file`() = doTest( 18 | """ 19 | $DummyHeader 20 | 21 | void foo() :{}{ 22 | "foo" #f 23 | } 24 | 25 | """, 26 | """ 27 | PsiFileStubImpl 28 | BNF_PRODUCTION:BnfProductionStubImpl 29 | SCOPED_EXPANSION_UNIT:JccScopedExpansionUnitStub 30 | """.trimIndent() 31 | ) 32 | 33 | 34 | 35 | private fun doTest(@Language("JavaCC") code: String, expectedStubText: String) { 36 | val file = configureByText(code) 37 | val vFile = file.virtualFile!! 38 | runWriteAction { 39 | VfsUtil.saveText(vFile, code) 40 | } 41 | val stubTree = StubTreeLoader.getInstance().readFromVFile(project, vFile) ?: error("Stub tree is null") 42 | val stubText = DebugUtil.stubTreeToString(stubTree.root) 43 | assert(expectedStubText.trimIndent() + "\n" == stubText) 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/kotlin/com/github/oowekyala/ijcc/lang/psi/impl/JjtNodeClassOwnerImpl.kt: -------------------------------------------------------------------------------- 1 | package com.github.oowekyala.ijcc.lang.psi.impl 2 | 3 | import com.github.oowekyala.ijcc.lang.model.GrammarNature 4 | import com.github.oowekyala.ijcc.lang.psi.JjtNodeClassOwner 5 | import com.github.oowekyala.ijcc.lang.psi.nodeIdentifier 6 | import com.github.oowekyala.ijcc.lang.psi.stubs.JjtNodeClassOwnerStub 7 | import com.intellij.lang.ASTNode 8 | import com.intellij.psi.stubs.IStubElementType 9 | 10 | abstract class JjtNodeClassOwnerImpl> 11 | : JccStubBasedPsiElementImpl, JjtNodeClassOwner { 12 | 13 | 14 | constructor(node: ASTNode) : super(node) 15 | 16 | constructor(stub: TStub, stubType: IStubElementType) : super(stub, stubType) 17 | 18 | 19 | override val nodeQualifiedName: String? 20 | get() = stub?.jjtNodeQualifiedName ?: nodeSimpleName?.let { 21 | val packageName = grammarOptions.nodePackage 22 | 23 | if (packageName.isEmpty()) nodeSimpleName 24 | else "$packageName.$it" 25 | } 26 | 27 | override val nodeSimpleName: String? 28 | get() = stub?.jjtNodeQualifiedName?.substringAfterLast('.') 29 | ?: nodeRawName?.let { grammarOptions.nodePrefix + it } 30 | 31 | override val nodeRawName: String? 32 | get() = stub?.jjtNodeRawName ?: nodeIdentifier?.name?.takeIf { 33 | // nothing generates nodes in jj files 34 | containingFile.grammarNature >= GrammarNature.JJTREE 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /src/main/kotlin/com/github/oowekyala/ijcc/lang/psi/TraversalUtil.kt: -------------------------------------------------------------------------------- 1 | package com.github.oowekyala.ijcc.lang.psi 2 | 3 | import com.intellij.lang.parser.GeneratedParserUtilBase 4 | import com.intellij.openapi.util.Conditions 5 | import com.intellij.psi.PsiElement 6 | import com.intellij.psi.SyntaxTraverser 7 | import com.intellij.psi.SyntaxTraverser.psiTraverser 8 | import com.intellij.psi.tree.IElementType 9 | 10 | /** 11 | * @author Clément Fournier 12 | * @since 1.0 13 | */ 14 | 15 | 16 | fun grammarTraverser(root: PsiElement): SyntaxTraverser = 17 | psiTraverser().withRoot(root) 18 | .forceDisregardTypes(Conditions.equalTo(GeneratedParserUtilBase.DUMMY_BLOCK)) 19 | .filter(Conditions.instanceOf(JccPsiElement::class.java)) 20 | 21 | fun grammarTraverserNoJava(root: PsiElement): SyntaxTraverser = 22 | grammarTraverser(root) 23 | .forceIgnore { 24 | when (it) { 25 | is JccJavaCompilationUnit, is JccJavaBlock, is JccJavaExpression -> true 26 | else -> false 27 | } 28 | } 29 | 30 | fun grammarTraverserOnlyBnf(root: PsiElement): SyntaxTraverser = 31 | grammarTraverserNoJava(root) 32 | .forceIgnore(Conditions.instanceOf(JccOptionSection::class.java)) 33 | .forceIgnore(Conditions.instanceOf(JccRegexProduction::class.java)) 34 | 35 | -------------------------------------------------------------------------------- /src/main/kotlin/com/github/oowekyala/ijcc/ide/quickdoc/JccOptionDocMaker.kt: -------------------------------------------------------------------------------- 1 | package com.github.oowekyala.ijcc.ide.quickdoc 2 | 3 | import com.github.oowekyala.ijcc.ide.quickdoc.HtmlUtil.grayed 4 | import com.github.oowekyala.ijcc.ide.quickdoc.JccDocUtil.buildQuickDoc 5 | import com.github.oowekyala.ijcc.lang.model.GenericOption 6 | import com.github.oowekyala.ijcc.lang.model.IGrammarOptions 7 | import com.github.oowekyala.ijcc.lang.model.presentValue 8 | import com.github.oowekyala.ijcc.lang.psi.JccOptionBinding 9 | 10 | /** 11 | * @author Clément Fournier 12 | * @since 1.2 13 | */ 14 | object JccOptionDocMaker { 15 | 16 | fun makeDoc(binding: JccOptionBinding?, 17 | ctx: IGrammarOptions, 18 | opt: GenericOption<*>): String = buildQuickDoc { 19 | definition { 20 | "Option ${HtmlUtil.bold(opt.name)} " + grayed("(${opt.supportedNature.displayName})") 21 | } 22 | 23 | sections { 24 | section("Current value") { 25 | val curValue = opt.getValue(binding, ctx).presentValue() 26 | val default = opt.contextualDefaultValue(ctx).presentValue() 27 | 28 | val tail = when { 29 | curValue == default -> "(=default)" 30 | else -> "(default $default)" 31 | } 32 | 33 | "$curValue ${grayed(tail)}" 34 | } 35 | } 36 | 37 | freeHtml { 38 | opt.description ?: HtmlUtil.emph("(no description)") 39 | } 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /src/test/kotlin/com/github/oowekyala/ijcc/ide/inspections/ConsecutiveParserActionsInspectionTest.kt: -------------------------------------------------------------------------------- 1 | package com.github.oowekyala.ijcc.ide.inspections 2 | 3 | /** 4 | * @author Clément Fournier 5 | * @since 1.1 6 | */ 7 | class ConsecutiveParserActionsInspectionTest : JccInspectionTestBase(ConsecutiveParserActionsInspection()) { 8 | 9 | private fun String.warn() = warningAnnot(this, ConsecutiveParserActionsInspection.ProblemDescription) 10 | 11 | 12 | fun `test warning range`() = checkByText(""" 13 | $DummyHeader 14 | 15 | 16 | void Foo(): 17 | {} 18 | { 19 | "foo" "bar" ${"{foo();} {bar;} {lol.fo();}".warn()} "flab" {weLiveInASociety();} 20 | } 21 | 22 | 23 | """) 24 | 25 | fun `test warning range in node scope`() = checkByText(""" 26 | $DummyHeader 27 | 28 | 29 | void Foo(): 30 | {} 31 | { 32 | ("foo" "bar" ${"{foo();} {bar;}".warn()} {lol.fo();}) #F "flab" {weLiveInASociety();} 33 | } 34 | 35 | 36 | """) 37 | 38 | fun `test warning range in node scope 2`() = checkByText(""" 39 | $DummyHeader 40 | 41 | 42 | void Foo(): 43 | {} 44 | { 45 | "foo" "bar" ${"{foo();} {bar;}".warn()} {lol.fo();} 46 | } 47 | 48 | 49 | """) 50 | 51 | fun `test warning range in node scope 3`() = checkByText(""" 52 | $DummyHeader 53 | 54 | 55 | void Foo(): 56 | {} 57 | { 58 | "foo" "bar" {foo();} {bar;} 59 | } 60 | 61 | 62 | """) 63 | 64 | 65 | } 66 | -------------------------------------------------------------------------------- /src/main/kotlin/com/github/oowekyala/ijcc/ide/findusages/JjtreeNodeReferenceSearcher.kt: -------------------------------------------------------------------------------- 1 | package com.github.oowekyala.ijcc.ide.findusages 2 | 3 | import com.github.oowekyala.ijcc.ide.refs.JjtNodePolyReference 4 | import com.github.oowekyala.ijcc.lang.psi.* 5 | import com.intellij.openapi.application.QueryExecutorBase 6 | import com.intellij.psi.PsiReference 7 | import com.intellij.psi.search.searches.ReferencesSearch 8 | import com.intellij.util.Processor 9 | 10 | /** 11 | * @author Clément Fournier 12 | * @since 1.1 13 | */ 14 | class JjtreeNodeReferenceSearcher : QueryExecutorBase(true) { 15 | 16 | override fun processQuery(queryParameters: ReferencesSearch.SearchParameters, 17 | consumer: Processor) { 18 | 19 | val toSearch = queryParameters.elementToSearch as? JccIdentifier ?: return 20 | 21 | if (!toSearch.isJjtreeNodeIdentifier) return 22 | 23 | val owner = toSearch.firstAncestorOrNull() ?: return 24 | 25 | findReferencesTo(owner, toSearch.name).all { 26 | // "all" stops on the first "false" result 27 | consumer.process(it) 28 | } 29 | } 30 | 31 | private fun findReferencesTo(origin: JjtNodeClassOwner, 32 | rawName: String): Sequence = 33 | origin.containingFile 34 | .getJjtreeDeclsForRawName(rawName) 35 | .asSequence() 36 | .filter { it != origin } 37 | .mapNotNull { it.typedReference } 38 | 39 | 40 | } 41 | -------------------------------------------------------------------------------- /src/test/kotlin/com/github/oowekyala/ijcc/ide/inspections/JccInspectionSuppressorTest.kt: -------------------------------------------------------------------------------- 1 | package com.github.oowekyala.ijcc.ide.inspections 2 | 3 | /** 4 | * @author Clément Fournier 5 | * @since 1.0 6 | */ 7 | class JccInspectionSuppressorTest : JccInspectionTestBase(LookaheadIsNotAtChoicePointInspection()) { 8 | 9 | 10 | fun testSuppressSingleComment() = checkByText( 11 | """ 12 | $DummyHeader 13 | 14 | //noinspection LookaheadIsNotAtChoicePoint 15 | void Foo(): {} 16 | { 17 | LOOKAHEAD(1) "foo" "bar" 18 | } 19 | 20 | """.trimIndent() 21 | ) 22 | 23 | 24 | fun testSuppressCStyleComment() = checkByText( 25 | """ 26 | $DummyHeader 27 | 28 | /* noinspection LookaheadIsNotAtChoicePoint */ 29 | void Foo(): {} 30 | { 31 | LOOKAHEAD(1) "foo" "bar" 32 | } 33 | 34 | """.trimIndent() 35 | ) 36 | 37 | 38 | fun testSuppressAllEol() = checkByText( 39 | """ 40 | $DummyHeader 41 | 42 | //noinspection ALL 43 | void Foo(): {} 44 | { 45 | LOOKAHEAD(1) "foo" "bar" 46 | } 47 | 48 | """.trimIndent() 49 | ) 50 | 51 | fun testSuppressCStyleInlineComment() = checkByText( 52 | """ 53 | $DummyHeader 54 | 55 | 56 | void Foo(): {} 57 | { 58 | /* noinspection LookaheadIsNotAtChoicePoint */ LOOKAHEAD(1) "foo" "bar" 59 | } 60 | 61 | """.trimIndent() 62 | ) 63 | } 64 | -------------------------------------------------------------------------------- /src/main/kotlin/com/github/oowekyala/ijcc/lang/psi/manipulators/JccJavaCompilationUnitManipulator.kt: -------------------------------------------------------------------------------- 1 | package com.github.oowekyala.ijcc.lang.psi.manipulators 2 | 3 | import com.github.oowekyala.ijcc.lang.JccTypes 4 | import com.github.oowekyala.ijcc.lang.psi.JccJavaCompilationUnit 5 | import com.github.oowekyala.ijcc.lang.psi.impl.jccEltFactory 6 | import com.github.oowekyala.ijcc.lang.psi.isOfType 7 | import com.github.oowekyala.ijcc.lang.psi.siblingSequence 8 | import com.intellij.openapi.util.TextRange 9 | import com.intellij.psi.AbstractElementManipulator 10 | 11 | 12 | /** 13 | * @author Clément Fournier 14 | * @since 1.0 15 | */ 16 | class JccJavaCompilationUnitManipulator : AbstractElementManipulator() { 17 | 18 | 19 | override fun getRangeInElement(element: JccJavaCompilationUnit): TextRange { 20 | val braceOffset = element.lastChild.siblingSequence(false).firstOrNull { it.isOfType(JccTypes.JCC_RBRACE) } ?: return super.getRangeInElement(element) 21 | return TextRange.from(0, braceOffset.textRange.startOffset - element.textRange.startOffset) 22 | } 23 | 24 | 25 | override fun handleContentChange(element: JccJavaCompilationUnit, 26 | range: TextRange, 27 | newContent: String?): JccJavaCompilationUnit? { 28 | val oldText = element.text 29 | val newText = oldText.substring(0, range.startOffset) + newContent + 30 | oldText.substring(range.endOffset) 31 | return element.replace(element.project.jccEltFactory.createJcu(newText)) as JccJavaCompilationUnit 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/kotlin/com/github/oowekyala/ijcc/ide/folding/JccFoldingOptionsProvider.kt: -------------------------------------------------------------------------------- 1 | package com.github.oowekyala.ijcc.ide.folding 2 | 3 | import com.github.oowekyala.ijcc.settings.JccGlobalSettingsState 4 | import com.github.oowekyala.ijcc.settings.globalPluginSettings 5 | import com.intellij.application.options.editor.CodeFoldingOptionsProvider 6 | import com.intellij.openapi.options.BeanConfigurable 7 | import kotlin.reflect.KMutableProperty 8 | 9 | /** 10 | * @author Clément Fournier 11 | */ 12 | class JccFoldingOptionsProvider : 13 | BeanConfigurable(globalPluginSettings, "JavaCC"), CodeFoldingOptionsProvider { 14 | 15 | init { 16 | val settings = globalPluginSettings 17 | 18 | fun checkBox(title: String, prop: KMutableProperty) = 19 | checkBox(title, { prop.getter.call() }, { prop.setter.call(it) }) 20 | 21 | checkBox("Java fragments in JavaCC code", settings::isFoldJavaFragments) 22 | checkBox("JavaCC local lookahead declarations", settings::isFoldLookaheads) 23 | checkBox("JavaCC token references that can be replaced by a string literal", settings::isFoldTokenRefs) 24 | checkBox("JavaCC options section", settings::isFoldOptions) 25 | checkBox("JavaCC parser declaration section", settings::isFoldParserDecl) 26 | checkBox("JavaCC token manager declaration section", settings::isFoldTokenMgrDecl) 27 | checkBox("JavaCC regular expression productions (token declarations)", settings::isFoldTokenProds) 28 | checkBox("Generated sections in .jj files (@bgen ... @egen)", settings::isFoldBgenSections) 29 | 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /src/main/kotlin/com/github/oowekyala/ijcc/settings/InjectionSupportLevel.kt: -------------------------------------------------------------------------------- 1 | package com.github.oowekyala.ijcc.settings 2 | 3 | import com.github.oowekyala.ijcc.ide.highlight.InjectedJavaHighlightVisitor 4 | import com.github.oowekyala.ijcc.lang.injection.JavaccLanguageInjector 5 | import org.intellij.lang.annotations.Language 6 | 7 | /** 8 | * Level of Java injection the plugin performs. 9 | * 10 | * @author Clément Fournier 11 | * @since 1.0 12 | */ 13 | enum class InjectionSupportLevel(val displayName: String, val description: String) { 14 | 15 | /** No injection. */ 16 | DISABLED("Disabled", "No injection"), 17 | 18 | // TODO maybe have a level with a basic, fast injection scheme with no control flow reconstruction 19 | // if performance is still too bad for some users 20 | 21 | /** Enables [JavaccLanguageInjector]*/ 22 | @Language("HTML") 23 | CONSERVATIVE( 24 | "Partial", 25 | """ 26 | Basic highlighting, code completion, quick documentation, 27 | usage resolution, control-flow analysis, inspections, etc. 28 | This level is already extremely potent, but compilation errors 29 | are not flagged. 30 | """.trimIndent() 31 | ), 32 | 33 | /** Enables [InjectedJavaHighlightVisitor]. */ 34 | @Language("HTML") 35 | FULL( 36 | "Full", 37 | """ 38 | Adds compilation error checking, including type checking — 39 | everything is like a regular Java file. Be aware that highlighting 40 | updates are most of the time quite slow. 41 | """.trimIndent() 42 | ) 43 | } 44 | -------------------------------------------------------------------------------- /src/test/kotlin/com/github/oowekyala/ijcc/ide/inspections/LoopInRegexInspectionTest.kt: -------------------------------------------------------------------------------- 1 | package com.github.oowekyala.ijcc.ide.inspections 2 | 3 | import com.github.oowekyala.ijcc.ide.inspections.LoopInRegexInspection.Companion.makeMessageImpl 4 | 5 | /** 6 | * @author Clément Fournier 7 | * @since 1.1 8 | */ 9 | class LoopInRegexInspectionTest : JccInspectionTestBase(LoopInRegexInspection()) { 10 | 11 | 12 | private fun String.warning(vararg path: String) = 13 | errorAnnot(trimIndent(), makeMessageImpl(path.toList())) 14 | 15 | 16 | fun testSelfRecursion() = checkByText( 17 | """ 18 | $DummyHeader 19 | 20 | TOKEN: { 21 | ${">".warning("FOO", "FOO")} 22 | } 23 | """ 24 | ) 25 | 26 | fun testMutualRecursion() = checkByText( 27 | """ 28 | $DummyHeader 29 | TOKEN:{ 30 | ${">".warning("FOO", "BAR", "FOO")} 31 | | > 32 | } 33 | """ 34 | ) 35 | 36 | fun testIndirectRecursion() = checkByText( 37 | """ 38 | $DummyHeader 39 | TOKEN:{ 40 | ${">".warning("FOO", "BAR", "BAZ", "FOO")} 41 | | > 42 | | > 43 | } 44 | 45 | """ 46 | ) 47 | 48 | fun testLoopAtAnyPos() = checkByText( 49 | """ 50 | $DummyHeader 51 | TOKEN:{ 52 | ${">".warning("FOO", "BAR", "BAZ", "FOO")} 53 | | > 54 | | > 55 | } 56 | 57 | """ 58 | ) 59 | 60 | 61 | } -------------------------------------------------------------------------------- /src/test/kotlin/com/github/oowekyala/ijcc/ide/inspections/RegexMayMatchEmptyStringInspectionTest.kt: -------------------------------------------------------------------------------- 1 | package com.github.oowekyala.ijcc.ide.inspections 2 | 3 | import com.github.oowekyala.ijcc.ide.inspections.RegexMayMatchEmptyStringInspection.Companion.makeMessage 4 | import com.github.oowekyala.ijcc.lang.model.LexicalState 5 | 6 | /** 7 | * @author Clément Fournier 8 | * @since 1.1 9 | */ 10 | class RegexMayMatchEmptyStringInspectionTest : JccInspectionTestBase(RegexMayMatchEmptyStringInspection()) { 11 | 12 | private fun String.warning(name: String? = "FOO", 13 | states: List = LexicalState.JustDefaultState) = 14 | warningAnnot(trimIndent(), makeMessage(name, states)) 15 | 16 | 17 | fun testEasyNoName() = checkByText( 18 | """ 19 | $DummyHeader 20 | 21 | TOKEN: { 22 | ${"\"\"".warning(null)} 23 | } 24 | """ 25 | ) 26 | 27 | 28 | fun testEasyWithName() = checkByText( 29 | """ 30 | $DummyHeader 31 | 32 | TOKEN: { 33 | 34 | } 35 | """ 36 | ) 37 | 38 | fun testWithAlternative() = checkByText( 39 | """ 40 | $DummyHeader 41 | 42 | TOKEN: { 43 | 44 | } 45 | """ 46 | ) 47 | 48 | 49 | fun testWithRef() = checkByText( 50 | """ 51 | $DummyHeader 52 | TOKEN:{ 53 | )?".warning()}> 54 | | > 55 | | 56 | } 57 | 58 | """ 59 | ) 60 | 61 | 62 | } -------------------------------------------------------------------------------- /src/main/kotlin/com/github/oowekyala/ijcc/JavaccFileType.kt: -------------------------------------------------------------------------------- 1 | package com.github.oowekyala.ijcc 2 | 3 | import com.github.oowekyala.ijcc.icons.JccCoreIcons 4 | import com.intellij.openapi.fileTypes.LanguageFileType 5 | import javax.swing.Icon 6 | 7 | private val JavaccLang = JavaccLanguage.newInstance() 8 | 9 | abstract class BaseJccFileType : LanguageFileType(JavaccLang) { 10 | override fun getDisplayName(): String = description 11 | } 12 | 13 | /** 14 | * @since inception 15 | */ 16 | class JavaccFileType : BaseJccFileType() { 17 | override fun getIcon(): Icon = JccCoreIcons.JAVACC_FILE 18 | 19 | override fun getName(): String = "JAVACC_GRAMMAR" 20 | 21 | override fun getDefaultExtension(): String = "jj" 22 | 23 | override fun getDescription(): String = "JavaCC grammar" 24 | } 25 | 26 | /** 27 | * This second file type is available for JJTree files. 28 | * 29 | * @since 1.2 30 | */ 31 | class JjtreeFileType : BaseJccFileType() { 32 | override fun getIcon(): Icon = JccCoreIcons.JJTREE_FILE 33 | 34 | override fun getName(): String = "JJTREE_GRAMMAR" 35 | 36 | override fun getDefaultExtension(): String = "jjt" 37 | 38 | override fun getDescription(): String = "JJTree grammar" 39 | } 40 | 41 | /** 42 | * This file type is available for Javacc 21. 43 | * 44 | * @since 1.6 45 | */ 46 | class Javacc21FileType : LanguageFileType(CongoccLanguage.newInstance()) { 47 | override fun getDisplayName(): String = description 48 | override fun getIcon(): Icon = JccCoreIcons.JAVACC_FILE 49 | 50 | override fun getName(): String = "JAVACC21_GRAMMAR" 51 | 52 | override fun getDefaultExtension(): String = "ccc" 53 | 54 | override fun getDescription(): String = "CongoCC grammar" 55 | } 56 | -------------------------------------------------------------------------------- /src/main/kotlin/com/github/oowekyala/ijcc/ide/gutter/JjtreeNodeClassLineMarkerProvider.kt: -------------------------------------------------------------------------------- 1 | package com.github.oowekyala.ijcc.ide.gutter 2 | 3 | import com.github.oowekyala.ijcc.icons.JccIcons 4 | import com.github.oowekyala.ijcc.lang.psi.JccNonTerminalProduction 5 | import com.github.oowekyala.ijcc.lang.psi.JccScopedExpansionUnit 6 | import com.github.oowekyala.ijcc.lang.psi.JjtNodeClassOwner 7 | import com.github.oowekyala.ijcc.lang.psi.nodeClass 8 | import com.intellij.codeInsight.daemon.RelatedItemLineMarkerInfo 9 | import com.intellij.codeInsight.navigation.NavigationGutterIconBuilder 10 | import com.intellij.psi.PsiElement 11 | 12 | /** 13 | * Adds a gutter icon linking a production to a JJTree node class. 14 | * 15 | * @author Clément Fournier 16 | * @since 1.0 17 | */ 18 | class JjtreeNodeClassLineMarkerProvider : 19 | BaseTargetingLineMarkerProvider(JjtNodeClassOwner::class.java) { 20 | 21 | override fun processElt(elt: JjtNodeClassOwner): Sequence> { 22 | val psiClass = elt.nodeClass ?: return emptySequence() 23 | 24 | val builder = NavigationGutterIconBuilder.create(JccIcons.GUTTER_NODE_CLASS).setTarget(psiClass) 25 | .setTooltipText("Navigate to class ${psiClass.name}") 26 | .setPopupTitle("Class ${psiClass.name}") 27 | 28 | val markerBearer = when (elt) { 29 | is JccNonTerminalProduction -> elt.nameIdentifier 30 | is JccScopedExpansionUnit -> elt.jjtreeNodeDescriptor.nameIdentifier 31 | else -> null 32 | }?.leaf 33 | 34 | return markerBearer?.let { builder.createLineMarkerInfo(it) }?.let { sequenceOf(it) } ?: emptySequence() 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /src/main/kotlin/com/github/oowekyala/ijcc/ide/intentions/CheckRegexIntention.kt: -------------------------------------------------------------------------------- 1 | package com.github.oowekyala.ijcc.ide.intentions 2 | 3 | import com.github.oowekyala.ijcc.ide.quickdoc.JccTerminalDocMaker 4 | import com.github.oowekyala.ijcc.lang.psi.JccRefRegularExpression 5 | import com.github.oowekyala.ijcc.lang.psi.JccRegularExpression 6 | import com.github.oowekyala.ijcc.lang.psi.pattern 7 | import com.github.oowekyala.ijcc.util.runIt 8 | import com.intellij.codeInsight.intention.LowPriorityAction 9 | import com.intellij.codeInsight.intention.impl.QuickEditHandler 10 | import com.intellij.openapi.editor.Editor 11 | import com.intellij.openapi.project.Project 12 | import com.intellij.openapi.util.Iconable 13 | import org.intellij.lang.regexp.RegExpLanguage 14 | import javax.swing.Icon 15 | 16 | 17 | class CheckRegExpIntentionAction : 18 | JccSelfTargetingEditorIntentionBase( 19 | JccRegularExpression::class.java, 20 | "Check Regexp" 21 | ), 22 | LowPriorityAction, Iconable { 23 | 24 | 25 | override fun isApplicableTo(element: JccRegularExpression): Boolean = 26 | element !is JccRefRegularExpression && element.pattern != null 27 | 28 | override fun run(project: Project, editor: Editor, element: JccRegularExpression): () -> Unit = { 29 | 30 | element.pattern?.runIt { r -> 31 | val name = JccTerminalDocMaker.htmlNameOfToken(element.name) 32 | val component = CheckTokenRegexForm(r, name, project).rootPanel 33 | 34 | QuickEditHandler.showBalloon(editor, element.containingFile, component) 35 | 36 | } 37 | } 38 | 39 | override fun startInWriteAction(): Boolean = false 40 | 41 | override fun getIcon(flags: Int): Icon? = RegExpLanguage.INSTANCE.associatedFileType?.icon 42 | } 43 | -------------------------------------------------------------------------------- /src/main/kotlin/com/github/oowekyala/ijcc/JavaccPairedBraceMatcher.kt: -------------------------------------------------------------------------------- 1 | package com.github.oowekyala.ijcc 2 | 3 | import com.github.oowekyala.ijcc.lang.JccTypes.* 4 | import com.github.oowekyala.ijcc.lang.psi.JccJavaBlock 5 | import com.github.oowekyala.ijcc.lang.psi.JccNonTerminalProduction 6 | import com.intellij.lang.BracePair 7 | import com.intellij.lang.PairedBraceMatcher 8 | import com.intellij.psi.PsiFile 9 | import com.intellij.psi.tree.IElementType 10 | 11 | /** 12 | * @author Clément Fournier 13 | * @since 1.0 14 | */ 15 | class JavaccPairedBraceMatcher : PairedBraceMatcher { 16 | override fun getCodeConstructStart(file: PsiFile?, openingBraceOffset: Int): Int { 17 | val element = file?.findElementAt(openingBraceOffset) 18 | if (element == null || element is PsiFile) return openingBraceOffset 19 | 20 | var parent = element.parent 21 | if (parent is JccJavaBlock) { 22 | parent = parent.getParent() 23 | if (parent is JccNonTerminalProduction) { 24 | return parent.textRange.startOffset 25 | } 26 | } else if (parent is JccNonTerminalProduction) { 27 | return parent.textRange.startOffset 28 | } 29 | return openingBraceOffset 30 | } 31 | 32 | 33 | override fun getPairs(): Array = Companion.pairs 34 | 35 | override fun isPairedBracesAllowedBeforeType(lbraceType: IElementType, contextType: IElementType?): Boolean = true 36 | 37 | companion object { 38 | private val pairs = arrayOf( 39 | BracePair(JCC_LPARENTH, JCC_RPARENTH, true), // TODO structural? 40 | BracePair(JCC_LBRACE, JCC_RBRACE, true), 41 | BracePair(JCC_LBRACKET, JCC_RBRACKET, false), 42 | BracePair(JCC_LT, JCC_GT, false) 43 | ) 44 | } 45 | } -------------------------------------------------------------------------------- /src/main/kotlin/com/github/oowekyala/ijcc/ide/completion/JccPatterns.kt: -------------------------------------------------------------------------------- 1 | package com.github.oowekyala.ijcc.ide.completion 2 | 3 | import com.github.oowekyala.ijcc.lang.JccTypes 4 | import com.github.oowekyala.ijcc.lang.psi.JccBnfProduction 5 | import com.github.oowekyala.ijcc.lang.psi.JccFile 6 | import com.github.oowekyala.ijcc.lang.psi.JccOptionBinding 7 | import com.intellij.patterns.ElementPattern 8 | import com.intellij.patterns.PlatformPatterns.instanceOf 9 | import com.intellij.patterns.PlatformPatterns.psiElement 10 | import com.intellij.psi.PsiComment 11 | import com.intellij.psi.PsiElement 12 | import com.intellij.psi.TokenType 13 | 14 | /** 15 | * Patterns for auto completion. 16 | * 17 | * TODO we can use those to simplify live template contexts 18 | * 19 | * @author Clément Fournier 20 | * @since 1.2 21 | */ 22 | object JccPatterns { 23 | 24 | val placePattern: ElementPattern = psiElement() 25 | .inFile(instanceOf(JccFile::class.java)) 26 | .andNot(psiElement().inside(PsiComment::class.java)) 27 | 28 | val optionValuePattern: ElementPattern = 29 | psiElement() 30 | .withAncestor(2, psiElement(JccOptionBinding::class.java)) 31 | .afterLeafSkipping(psiElement(TokenType.ERROR_ELEMENT), psiElement(JccTypes.JCC_EQ)) 32 | 33 | val optionNamePattern: ElementPattern = 34 | psiElement() 35 | .atStartOf(psiElement(JccOptionBinding::class.java)) 36 | .andNot(psiElement().inside(PsiComment::class.java)) 37 | .andNot(optionValuePattern) 38 | 39 | val bnfColonPattern = 40 | psiElement(JccTypes.JCC_COLON).withParent(JccBnfProduction::class.java) 41 | 42 | val jjtreeHashPattern = 43 | psiElement().afterLeaf(psiElement(JccTypes.JCC_POUND)) 44 | 45 | } 46 | -------------------------------------------------------------------------------- /src/main/kotlin/com/github/oowekyala/ijcc/ide/refs/JccNonTerminalReference.kt: -------------------------------------------------------------------------------- 1 | package com.github.oowekyala.ijcc.ide.refs 2 | 3 | import com.github.oowekyala.ijcc.lang.psi.* 4 | import com.github.oowekyala.ijcc.lang.psi.manipulators.JccIdentifierManipulator 5 | import com.intellij.openapi.util.TextRange 6 | import com.intellij.psi.PsiElement 7 | import com.intellij.psi.PsiReferenceBase 8 | 9 | 10 | /** 11 | * Reference to a [JccNonTerminalProduction]. 12 | * 13 | * @author Clément Fournier 14 | * @since 1.0 15 | */ 16 | class JccNonTerminalReference(psiElement: JccNonTerminalExpansionUnit) : 17 | PsiReferenceBase(psiElement) { 18 | 19 | override fun resolve(): JccIdentifier? = resolveProduction()?.nameIdentifier 20 | 21 | fun resolveProduction(): JccNonTerminalProduction? { 22 | val searchedName = element.name ?: return null 23 | 24 | return element.containingFile.getProductionByName(searchedName) 25 | } 26 | 27 | override fun isReferenceTo(elt: PsiElement): Boolean { 28 | return when (elt) { 29 | is JccNonTerminalProduction -> elt.name == element.name 30 | is JccIdentifier -> elt.owner?.let { isReferenceTo(it) } == true 31 | else -> false 32 | } 33 | } 34 | 35 | 36 | override fun getVariants(): Array = 37 | JccRefVariantService.getInstance(element.project).nonterminalRefVariants(this) 38 | 39 | override fun calculateDefaultRangeInElement(): TextRange = element.nameIdentifier.textRangeInParent 40 | 41 | override fun handleElementRename(newElementName: String): PsiElement = newElementName.let { 42 | val id = element.nameIdentifier 43 | JccIdentifierManipulator().handleContentChange(id, newElementName)!! 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /src/main/kotlin/com/github/oowekyala/ijcc/lang/psi/stubs/indices/JccParserQnameIndexer.kt: -------------------------------------------------------------------------------- 1 | package com.github.oowekyala.ijcc.lang.psi.stubs.indices 2 | 3 | import com.github.oowekyala.ijcc.JavaccFileType 4 | import com.github.oowekyala.ijcc.JjtreeFileType 5 | import com.github.oowekyala.ijcc.lang.model.parserQualifiedName 6 | import com.github.oowekyala.ijcc.lang.psi.JccFile 7 | import com.intellij.util.indexing.* 8 | import com.intellij.util.io.EnumeratorStringDescriptor 9 | import com.intellij.util.io.KeyDescriptor 10 | 11 | 12 | /** 13 | * Indexes the qualified name of the parser class of a grammar file, 14 | * in order to provide links from the generated parser to the grammar. 15 | * 16 | * @author Clément Fournier 17 | * @since 1.2 18 | */ 19 | class JccParserQnameIndexer : ScalarIndexExtension() { 20 | 21 | override fun getName(): ID = NAME 22 | 23 | override fun getVersion(): Int = 1 24 | 25 | override fun dependsOnFileContent(): Boolean = true // FIXME? 26 | 27 | override fun getIndexer(): DataIndexer = MyDataIndexer 28 | 29 | override fun getInputFilter(): FileBasedIndex.InputFilter = 30 | DefaultFileTypeSpecificInputFilter(JavaccFileType(), JjtreeFileType()) 31 | 32 | override fun getKeyDescriptor(): KeyDescriptor = EnumeratorStringDescriptor.INSTANCE 33 | 34 | 35 | private object MyDataIndexer : DataIndexer { 36 | override fun map(inputData: FileContent): Map { 37 | 38 | val file = inputData.psiFile as? JccFile ?: return emptyMap() 39 | 40 | return mapOf(file.grammarOptions.parserQualifiedName to null) 41 | } 42 | } 43 | 44 | companion object { 45 | val NAME = ID.create("jccParserQname") 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /src/main/kotlin/com/github/oowekyala/ijcc/ide/findusages/JccUsageTypeProvider.kt: -------------------------------------------------------------------------------- 1 | package com.github.oowekyala.ijcc.ide.findusages 2 | 3 | import com.github.oowekyala.ijcc.lang.psi.* 4 | import com.intellij.psi.PsiElement 5 | import com.intellij.usages.UsageTarget 6 | import com.intellij.usages.impl.rules.UsageType 7 | import com.intellij.usages.impl.rules.UsageTypeProviderEx 8 | 9 | /** 10 | * @author Clément Fournier 11 | * @since 1.2 12 | */ 13 | class JccUsageTypeProvider : UsageTypeProviderEx { 14 | 15 | override fun getUsageType(element: PsiElement, targets: Array): UsageType? = when { 16 | element is JccNonTerminalExpansionUnit -> JccUsageTypes.NONTERMINAL_REFERENCE 17 | element is JccLiteralRegexUnit -> JccUsageTypes.IMPLICIT_STRING_TOKEN_REFERENCE 18 | element is JccTokenReferenceRegexUnit -> JccUsageTypes.EXPLICIT_TOKEN_REFERENCE 19 | element is JccIdentifier && element.isJjtreeNodeIdentifier -> JccUsageTypes.JJTREE_NODE_PARTIAL_DECLARATION 20 | element is JjtNodeClassOwner -> JccUsageTypes.JJTREE_NODE_PARTIAL_DECLARATION 21 | else -> null 22 | } 23 | 24 | 25 | override fun getUsageType(element: PsiElement): UsageType? = 26 | getUsageType(element, UsageTarget.EMPTY_ARRAY) 27 | 28 | object JccUsageTypes { 29 | 30 | val NONTERMINAL_REFERENCE = UsageType { "Non-terminal reference" } 31 | val JJTREE_NODE_PARTIAL_DECLARATION = UsageType { "JJTree node partial declaration" } 32 | val IMPLICIT_STRING_TOKEN_REFERENCE = UsageType { "String token implicit reference" } 33 | val EXPLICIT_TOKEN_REFERENCE = UsageType { "Token explicit reference" } 34 | 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /src/main/kotlin/com/github/oowekyala/ijcc/ide/findusages/JccStringTokenFindUsagesHandlerFactory.kt: -------------------------------------------------------------------------------- 1 | package com.github.oowekyala.ijcc.ide.findusages 2 | 3 | import com.github.oowekyala.ijcc.lang.psi.* 4 | import com.intellij.find.findUsages.FindUsagesHandler 5 | import com.intellij.find.findUsages.FindUsagesHandlerFactory 6 | import com.intellij.psi.PsiElement 7 | import com.intellij.psi.PsiReference 8 | import com.intellij.psi.search.SearchScope 9 | 10 | 11 | class JccStringTokenFindUsagesHandlerFactory : FindUsagesHandlerFactory() { 12 | override fun createFindUsagesHandler(element: PsiElement, forHighlightUsages: Boolean): FindUsagesHandler? = 13 | when (element) { 14 | is JccLiteralRegexUnit -> 15 | element.typedReference!! 16 | .resolveToken(exact = true) 17 | ?.let { JccTokenFindUsagesHandler(it.psiElement!!) } 18 | 19 | is JccIdentifier -> JccTokenFindUsagesHandler(element.namedTokenDef!!) 20 | else -> null 21 | } 22 | 23 | override fun canFindUsages(element: PsiElement): Boolean = 24 | element is JccLiteralRegexUnit && element.typedReference != null 25 | || element is JccIdentifier && element.namedTokenDef != null 26 | 27 | class JccTokenFindUsagesHandler(regexOwner: JccRegularExpressionOwner) : FindUsagesHandler(regexOwner) { 28 | 29 | override fun findReferencesToHighlight(target: PsiElement, 30 | searchScope: SearchScope): MutableCollection { 31 | val realTarget = when (target) { 32 | is JccIdentifier -> target.namedTokenDef!! 33 | else -> target 34 | } 35 | 36 | return super.findReferencesToHighlight(realTarget, searchScope) 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/kotlin/com/github/oowekyala/ijcc/lang/psi/stubs/indices/JccStubElementTypeHolder.kt: -------------------------------------------------------------------------------- 1 | package com.github.oowekyala.ijcc.lang.psi.stubs.indices 2 | 3 | import com.github.oowekyala.ijcc.lang.psi.JccBnfProduction 4 | import com.github.oowekyala.ijcc.lang.psi.JccJavacodeProduction 5 | import com.github.oowekyala.ijcc.lang.psi.JccPsiElement 6 | import com.github.oowekyala.ijcc.lang.psi.JccScopedExpansionUnit 7 | import com.github.oowekyala.ijcc.lang.psi.stubs.BnfProductionStubImpl 8 | import com.github.oowekyala.ijcc.lang.psi.stubs.JavacodeProductionStubImpl 9 | import com.github.oowekyala.ijcc.lang.psi.stubs.JccFileStub 10 | import com.github.oowekyala.ijcc.lang.psi.stubs.JccScopedExpansionUnitStub 11 | import com.intellij.psi.stubs.IStubElementType 12 | import com.intellij.psi.stubs.StubElement 13 | import com.intellij.psi.tree.IElementType 14 | import com.intellij.psi.tree.StubFileElementType 15 | 16 | interface JccStubElementTypeHolder { 17 | companion object { 18 | @JvmField 19 | val bnfProd: StubElementType = cast(BnfProductionStubImpl.TYPE) 20 | 21 | @JvmField 22 | val javacodeProd: StubElementType = cast(JavacodeProductionStubImpl.TYPE) 23 | 24 | @JvmField 25 | val unitStub: StubElementType = cast(JccScopedExpansionUnitStub.TYPE) 26 | 27 | @JvmField 28 | val fileStub: StubFileElementType = JccFileStub.TYPE 29 | } 30 | } 31 | 32 | 33 | /** Is needed to avoid very long type names */ 34 | private typealias StubElementType = IStubElementType, T> 35 | 36 | @Suppress("UNCHECKED_CAST") 37 | private fun cast(type: IElementType): IStubElementType, T> { 38 | return type as IStubElementType, T> 39 | } 40 | -------------------------------------------------------------------------------- /src/main/kotlin/com/github/oowekyala/ijcc/ide/intentions/ReplaceSupersedingUsageWithReferenceIntentionFix.kt: -------------------------------------------------------------------------------- 1 | package com.github.oowekyala.ijcc.ide.intentions 2 | 3 | import com.github.oowekyala.ijcc.lang.psi.JccLiteralRegexUnit 4 | import com.github.oowekyala.ijcc.lang.psi.JccTokenReferenceRegexUnit 5 | import com.github.oowekyala.ijcc.lang.psi.impl.jccEltFactory 6 | import com.github.oowekyala.ijcc.lang.psi.safeReplace 7 | import com.intellij.codeInsight.FileModificationService 8 | import com.intellij.codeInsight.intention.IntentionAction 9 | import com.intellij.openapi.editor.Editor 10 | import com.intellij.openapi.project.Project 11 | import com.intellij.psi.PsiDocumentManager 12 | import com.intellij.psi.PsiFile 13 | 14 | /** 15 | * @author Clément Fournier 16 | * @since 1.2 17 | */ 18 | class ReplaceSupersedingUsageWithReferenceIntentionFix(private val toReplace: JccLiteralRegexUnit, 19 | private val tokenName: String) : IntentionAction { 20 | override fun startInWriteAction(): Boolean = true 21 | 22 | override fun getFamilyName(): String = "Replace superseding string with reference to this token" 23 | 24 | override fun isAvailable(project: Project, editor: Editor?, file: PsiFile?): Boolean = true 25 | 26 | override fun getText(): String = familyName 27 | 28 | override fun invoke(project: Project, editor: Editor?, file: PsiFile?) { 29 | if (editor == null) throw IllegalArgumentException("This intention requires an editor") 30 | 31 | 32 | FileModificationService.getInstance().prepareFileForWrite(toReplace.containingFile) 33 | val newRegexUnit: JccTokenReferenceRegexUnit = project.jccEltFactory.createRegexElement("<$tokenName>") 34 | 35 | toReplace.safeReplace(newRegexUnit) 36 | PsiDocumentManager.getInstance(project).doPostponedOperationsAndUnblockDocument(editor.document) 37 | 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/test/kotlin/com/github/oowekyala/ijcc/lang/util/TestExtensions.kt: -------------------------------------------------------------------------------- 1 | package com.github.oowekyala.ijcc.lang.util 2 | 3 | import io.kotest.matchers.Matcher 4 | import io.kotest.matchers.MatcherResult 5 | import io.kotest.matchers.shouldNotBe 6 | import io.kotest.matchers.should as ktShould 7 | import io.kotest.matchers.shouldBe as ktShouldBe 8 | 9 | 10 | fun > C?.eachShouldMatchInOrder(vararg assertions: (T) -> Unit) { 11 | 12 | this shouldNotBe null 13 | 14 | this!! // just to inform the type system 15 | 16 | 17 | this.zip(assertions).forEach { (item, matcher) -> 18 | matcher(item) 19 | } 20 | this.size ktShouldBe assertions.size 21 | } 22 | 23 | 24 | fun > C?.eachShouldMatchInOrder(vararg matchers: Matcher) { 25 | 26 | this shouldNotBe null 27 | 28 | this!! // just to inform the type system 29 | 30 | this.size ktShouldBe matchers.size 31 | 32 | this.zip(matchers).forEach { (item, matcher) -> 33 | item ktShould matcher 34 | } 35 | } 36 | 37 | fun String.normalizeWhiteSpace(): String = replace(Regex("""(\s|\R)+"""), " ").trim() 38 | 39 | fun Matcher.map(f: (B) -> A): Matcher = object : Matcher { 40 | override fun test(value: B): MatcherResult = this@map.test(f(value)) 41 | } 42 | 43 | 44 | inline fun stringMatchersIgnoreWhitespace(assertions: IgnoreWhitespaceCtx.() -> Unit): Unit = 45 | IgnoreWhitespaceCtx().assertions() 46 | 47 | /** Rebinds match methods for strings to something that normalizes whitespace before handling. */ 48 | class IgnoreWhitespaceCtx { 49 | 50 | 51 | infix fun String?.should(matcher: Matcher) { 52 | this?.normalizeWhiteSpace() ktShould matcher.map { it?.normalizeWhiteSpace() } 53 | } 54 | 55 | infix fun String?.shouldBe(value: String?) { 56 | this?.normalizeWhiteSpace() ktShouldBe value?.normalizeWhiteSpace() 57 | } 58 | 59 | } 60 | -------------------------------------------------------------------------------- /src/main/kotlin/com/github/oowekyala/ijcc/lang/psi/impl/JccScopedExpansionUnitImpl.kt: -------------------------------------------------------------------------------- 1 | // This is a generated file. Not intended for manual editing. 2 | package com.github.oowekyala.ijcc.lang.psi.impl 3 | 4 | import com.github.oowekyala.ijcc.ide.refs.JjtNodePolyReference 5 | import com.github.oowekyala.ijcc.lang.psi.* 6 | import com.github.oowekyala.ijcc.lang.psi.stubs.JccScopedExpansionUnitStub 7 | import com.intellij.lang.ASTNode 8 | import com.intellij.psi.PsiElementVisitor 9 | import com.intellij.psi.PsiReference 10 | import com.intellij.psi.stubs.IStubElementType 11 | import com.intellij.psi.util.PsiTreeUtil 12 | 13 | class JccScopedExpansionUnitImpl : JjtNodeClassOwnerImpl, JccScopedExpansionUnit { 14 | 15 | override val jjtreeNodeDescriptor: JccJjtreeNodeDescriptor 16 | get() = notNullChild( 17 | PsiTreeUtil.getChildOfType( 18 | this, 19 | JccJjtreeNodeDescriptor::class.java 20 | ) 21 | ) 22 | 23 | constructor(node: ASTNode) : super(node) 24 | 25 | constructor(stub: JccScopedExpansionUnitStub, stubType: IStubElementType) : super(stub, stubType) 26 | 27 | fun accept(visitor: JccVisitor) { 28 | visitor.visitScopedExpansionUnit(this) 29 | } 30 | 31 | override fun accept(visitor: PsiElementVisitor) { 32 | if (visitor is JccVisitor) 33 | accept(visitor) 34 | else 35 | super.accept(visitor) 36 | } 37 | 38 | override fun getReference(): PsiReference = JjtNodePolyReference(this) 39 | 40 | override val expansionUnit: JccExpansionUnit 41 | get() = notNullChild(PsiTreeUtil.getChildOfType(this, JccExpansionUnit::class.java)) 42 | 43 | override fun getNameIdentifier(): JccIdentifier? { 44 | val p1 = jjtreeNodeDescriptor 45 | return p1.nameIdentifier 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /src/main/kotlin/com/github/oowekyala/ijcc/ide/inspections/JccInspectionBase.kt: -------------------------------------------------------------------------------- 1 | package com.github.oowekyala.ijcc.ide.inspections 2 | 3 | import com.github.oowekyala.ijcc.JavaccLanguage 4 | import com.github.oowekyala.ijcc.lang.JccTypes.JCC_C_STYLE_COMMENT 5 | import com.github.oowekyala.ijcc.lang.JccTypes.JCC_END_OF_LINE_COMMENT 6 | import com.github.oowekyala.ijcc.lang.psi.JccTypesExt 7 | import com.github.oowekyala.ijcc.util.contains 8 | import com.intellij.codeInspection.InspectionProfileEntry 9 | import com.intellij.codeInspection.LocalInspectionTool 10 | import com.intellij.psi.PsiElement 11 | 12 | /** 13 | * Base class for inspections. 14 | * 15 | * @author Clément Fournier 16 | * @since 1.0 17 | */ 18 | abstract class JccInspectionBase(private val myDisplayName: String) : LocalInspectionTool() { 19 | // Since inspections can be suppressed inspection names and this scheme are published API 20 | // Basically the class name minus the "Inspection" suffix is the id 21 | // If the name collides with inspections of other languages, prefix the node class with "Jcc" 22 | // This is formalized in 1.3 23 | final override fun getID(): String = InspectionProfileEntry.getShortName(this::class.java.simpleName) 24 | 25 | final override fun getDisplayName(): String = myDisplayName 26 | final override fun getShortName(): String = id // short name must be unique wrt all other inspections 27 | final override fun getGroupDisplayName(): String = JavaccLanguage.displayName 28 | 29 | override fun isEnabledByDefault(): Boolean = true 30 | } 31 | 32 | val PsiElement.isJccComment: Boolean 33 | get() = JccTypesExt.CommentTypeSet.contains(this) 34 | 35 | val PsiElement.trimCommentMarkers: String 36 | get() = when (node.elementType) { 37 | JCC_END_OF_LINE_COMMENT -> text.removePrefix("//") 38 | JCC_C_STYLE_COMMENT -> text.removeSurrounding("/*", "*/") 39 | else -> text 40 | } 41 | 42 | -------------------------------------------------------------------------------- /src/main/kotlin/com/github/oowekyala/ijcc/ide/inspections/ActionWithinLookaheadInspection.kt: -------------------------------------------------------------------------------- 1 | package com.github.oowekyala.ijcc.ide.inspections 2 | 3 | import com.github.oowekyala.ijcc.ide.intentions.DeleteExpansionIntention 4 | import com.github.oowekyala.ijcc.lang.psi.JccLocalLookaheadUnit 5 | import com.github.oowekyala.ijcc.lang.psi.JccParserActionsUnit 6 | import com.github.oowekyala.ijcc.lang.psi.JccVisitor 7 | import com.github.oowekyala.ijcc.lang.psi.ancestors 8 | import com.intellij.codeInspection.ProblemsHolder 9 | import com.intellij.psi.PsiElementVisitor 10 | import org.intellij.lang.annotations.Language 11 | 12 | /** 13 | * @author Clément Fournier 14 | * @since 1.0 15 | */ 16 | class ActionWithinLookaheadInspection : JccInspectionBase(DisplayName) { 17 | 18 | 19 | @Language("HTML") 20 | override fun getStaticDescription() = """ 21 | Reports parser actions declared inside local lookahead specifications. 22 | Those are ignored during lookahead evaluation. 23 | """.trimIndent() 24 | 25 | 26 | override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor = 27 | object : JccVisitor() { 28 | override fun visitParserActionsUnit(o: JccParserActionsUnit) { 29 | 30 | if (o.ancestors(false).any { it is JccLocalLookaheadUnit }) { 31 | holder.registerProblem( 32 | o, 33 | ProblemDescription, 34 | DeleteExpansionIntention.quickFix( 35 | FixDescription, o.containingFile 36 | ) 37 | ) 38 | } 39 | } 40 | } 41 | 42 | 43 | companion object { 44 | const val DisplayName = "Parser actions within lookahead specifications are ignored" 45 | const val ProblemDescription = DisplayName 46 | const val FixDescription = "Delete expansion unit" 47 | } 48 | } -------------------------------------------------------------------------------- /src/main/kotlin/com/github/oowekyala/ijcc/lang/psi/JjtNodeClassOwner.kt: -------------------------------------------------------------------------------- 1 | package com.github.oowekyala.ijcc.lang.psi 2 | 3 | /** 4 | * Node that is tied to a generated node class, assumed to be 5 | * somewhere in the project. This is used to represent the 6 | * partial declarations of JJTree nodes. The methods return 7 | * non-null only if this psi element is associated with a 8 | * node, i.e. we're in a JJTree file and the node is not void. 9 | * 10 | * @author Clément Fournier 11 | * @since 1.0 12 | */ 13 | interface JjtNodeClassOwner : JccPsiElement, JccIdentifierOwner { 14 | 15 | // TODO 21 regex specs 16 | 17 | val jjtreeNodeDescriptor: JccJjtreeNodeDescriptor? 18 | 19 | val isVoid: Boolean 20 | get() = nodeRawName == null 21 | 22 | 23 | /** Gets the node's class qualified name. */ 24 | val nodeQualifiedName: String? 25 | 26 | /** Gets the node's simple name, accounting for the node prefix, etc. */ 27 | val nodeSimpleName: String? 28 | 29 | /** Name without the prefixes. */ 30 | val nodeRawName: String? 31 | 32 | } 33 | 34 | 35 | val JjtNodeClassOwner.isNotVoid: Boolean 36 | get() = !isVoid 37 | 38 | 39 | 40 | /** 41 | * Returns the identifier giving its name to the JJTree node. 42 | * It is null if the production or scoped expansion is #void. 43 | * It may differ from [JjtNodeClassOwner.getNameIdentifier] on 44 | * productions that have an annotation renaming them. 45 | */ 46 | val JjtNodeClassOwner.nodeIdentifier: JccIdentifier? 47 | get() = when (this) { 48 | is JccScopedExpansionUnit -> jjtreeNodeDescriptor.nameIdentifier 49 | is JccNonTerminalProduction -> jjtreeNodeDescriptor.let { 50 | if (it == null) 51 | if (grammarOptions.isDefaultVoid) null 52 | else this.nameIdentifier 53 | else it.nameIdentifier 54 | } 55 | else -> throw IllegalStateException() 56 | } 57 | 58 | -------------------------------------------------------------------------------- /src/main/kotlin/com/github/oowekyala/ijcc/ide/findusages/StringReferenceSearcher.kt: -------------------------------------------------------------------------------- 1 | package com.github.oowekyala.ijcc.ide.findusages 2 | 3 | import com.github.oowekyala.ijcc.ide.refs.JccBnfStringLiteralReference 4 | import com.github.oowekyala.ijcc.lang.psi.* 5 | import com.intellij.openapi.application.QueryExecutorBase 6 | import com.intellij.psi.PsiReference 7 | import com.intellij.psi.search.searches.ReferencesSearch 8 | import com.intellij.util.Processor 9 | 10 | /** 11 | * @author Clément Fournier 12 | * @since 1.1 13 | */ 14 | class StringReferenceSearcher : QueryExecutorBase(true) { 15 | override fun processQuery(queryParameters: ReferencesSearch.SearchParameters, 16 | consumer: Processor) { 17 | 18 | val toSearch = queryParameters.elementToSearch as? JccRegularExpressionOwner ?: return 19 | 20 | findReferencesTo(toSearch).all { 21 | // "all" stops on the first "false" result 22 | consumer.process(it) 23 | } 24 | 25 | } 26 | 27 | /* 28 | FIXME 29 | this is probably very inefficient, build indices in LexicalGrammar (or stubs!) 30 | we can't use the optimised word scan because it doesn't pick up on non-alphanumeric characters 31 | 32 | */ 33 | private fun findReferencesTo(target: JccRegularExpressionOwner): Sequence = 34 | target.containingFile 35 | .allProductions() 36 | .flatMap { it.descendantSequence(includeSelf = true) } 37 | .filterIsInstance() 38 | .mapNotNull { it.regularExpression.asSingleLiteral() } 39 | // filter out declaration 40 | .filter { it != target.definedToken.takeIf { it.isExplicit }?.asStringToken } 41 | .mapNotNull { it.typedReference } 42 | .filter { it.isReferenceTo(target) } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /src/test/resources/com/github/oowekyala/ijcc/lang/parser/fixtures/Tokens.jjt: -------------------------------------------------------------------------------- 1 | 2 | PARSER_BEGIN(JJTreeParser) 3 | 4 | PARSER_END(JJTreeParser) 5 | 6 | 7 | SPECIAL_TOKEN : 8 | { 9 | " " | "\t" | "\n" | "\r" | "\f" 10 | } 11 | 12 | SKIP : { <"(:"> { commentNestingDepth = 1; } : COMMENT_STATE } 13 | 14 | // Comments may be nested 15 | < COMMENT_STATE > SKIP : { "(:" { commentNestingDepth++; } } 16 | < COMMENT_STATE > SKIP : { ":)" { SwitchTo(--commentNestingDepth == 0 ? DEFAULT : COMMENT_STATE); } } 17 | < COMMENT_STATE > SKIP : { < ~[] > } 18 | 19 | 20 | TOKEN : 21 | { 22 | // Options is no longer a reserved word (Issue 126) 23 | // < _OPTIONS: "options" > 24 | < _LOOKAHEAD: "LOOKAHEAD" > 25 | | < _IGNORE_CASE: "IGNORE_CASE" > 26 | | < _PARSER_BEGIN: "PARSER_BEGIN" > 27 | | < _PARSER_END: "PARSER_END" > 28 | | < _JAVACODE: "JAVACODE" > 29 | | < _TOKEN: "TOKEN" > 30 | | < _SPECIAL_TOKEN: "SPECIAL_TOKEN" > 31 | | < _MORE: "MORE" > 32 | | < _SKIP: "SKIP" > 33 | | < _TOKEN_MGR_DECLS: "TOKEN_MGR_DECLS" > 34 | | < _EOF: "EOF" > 35 | } 36 | 37 | TOKEN : 38 | { 39 | < LEFT_WILDCARD : "*:" > 40 | | < RIGHT_WILDCARD : ":*" > 41 | | 42 | // The actual lexical grammar for NCName is: any name except * ":" * 43 | < NCNAME: ()* > 44 | | 45 | < EQNAME: (":" )? > 46 | | 47 | < #NAME_START_CHAR_NO_COLON: [ 48 | "A"-"Z", 49 | "a"-"z", 50 | "_", 51 | "\u00c0"-"\u00d6", 52 | "\u00d8"-"\u00f6", 53 | "\u00f8"-"\u02ff", 54 | "\u0370"-"\u037d", 55 | "\u037f"-"\u1fff", 56 | "\u200c"-"\u200d", 57 | "\u2070"-"\u218f", 58 | "\u2c00"-"\u2fef", 59 | "\u3001"-"\ud7ff", 60 | "\uf900"-"\ufdcf", 61 | "\ufdf0"-"\ufffd", 62 | "\u1000"-"\uefff" 63 | ] > 64 | | 65 | < #NAME_CHAR_NO_COLON: | [ 66 | "-", 67 | ".", 68 | "0"-"9", 69 | "\u00b7", 70 | "\u0300"-"\u036f", 71 | "\u203f"-"\u2040" 72 | ] > 73 | } 74 | -------------------------------------------------------------------------------- /src/main/kotlin/com/github/oowekyala/ijcc/ide/gutter/JjtreePartialDeclarationLineMarkerProvider.kt: -------------------------------------------------------------------------------- 1 | package com.github.oowekyala.ijcc.ide.gutter 2 | 3 | import com.github.oowekyala.ijcc.icons.JccIcons 4 | import com.github.oowekyala.ijcc.lang.psi.* 5 | import com.intellij.codeInsight.daemon.RelatedItemLineMarkerInfo 6 | import com.intellij.codeInsight.navigation.NavigationGutterIconBuilder 7 | import com.intellij.openapi.project.DumbAware 8 | import com.intellij.psi.PsiElement 9 | 10 | /** 11 | * Adds a gutter icon linking a node bearer to its other declarations. 12 | * 13 | * @author Clément Fournier 14 | * @since 1.0 15 | */ 16 | class JjtreePartialDeclarationLineMarkerProvider : 17 | BaseTargetingLineMarkerProvider(JjtNodeClassOwner::class.java), DumbAware { 18 | 19 | 20 | override fun processElt(elt: JjtNodeClassOwner): Sequence> = 21 | elt.typedReference 22 | ?.lightMultiResolve() 23 | ?.takeIf { it.size > 1 } 24 | ?.let { targets -> 25 | val nodeName = targets[0].nodeSimpleName!! 26 | 27 | NavigationGutterIconBuilder.create(JccIcons.GUTTER_PARTIAL_DECL) 28 | .setTargets(targets) 29 | .setCellRenderer { JjtPartialDeclCellRenderer } 30 | .setTooltipText("Navigate to partial declarations of $nodeName") 31 | .setPopupTitle("Select partial declaration for $nodeName") 32 | }?.let { builder -> 33 | when (elt) { 34 | is JccNonTerminalProduction -> elt.nameIdentifier 35 | is JccScopedExpansionUnit -> elt.nodeIdentifier 36 | else -> null 37 | }?.leaf?.let { 38 | builder.createLineMarkerInfo(it) 39 | } 40 | } 41 | ?.let { sequenceOf(it) } 42 | .orEmpty() 43 | 44 | 45 | } 46 | -------------------------------------------------------------------------------- /src/main/kotlin/com/github/oowekyala/ijcc/settings/JavaccAppSettingsService.kt: -------------------------------------------------------------------------------- 1 | package com.github.oowekyala.ijcc.settings 2 | 3 | import com.intellij.openapi.application.ApplicationManager 4 | import com.intellij.openapi.components.PersistentStateComponent 5 | import com.intellij.openapi.components.ServiceManager 6 | import com.intellij.openapi.components.State 7 | import com.intellij.openapi.components.Storage 8 | 9 | /** 10 | * App-level settings of the plugin. 11 | * 12 | * @author Clément Fournier 13 | * @since 1.0 14 | */ 15 | @State(name = "JavaccAppSettings", storages = [Storage("javacc-plugin.xml")]) 16 | class JavaccAppSettingsService : PersistentStateComponent { 17 | 18 | override fun loadState(state: JccGlobalSettingsState) { 19 | myState = state.copy() 20 | } 21 | 22 | private var myState = JccGlobalSettingsState() 23 | 24 | override fun getState(): JccGlobalSettingsState = myState 25 | 26 | } 27 | 28 | /** 29 | * App-level settings of the plugin. 30 | */ 31 | val globalPluginSettings: JccGlobalSettingsState 32 | get() = ApplicationManager.getApplication() 33 | .getService(JavaccAppSettingsService::class.java).state 34 | 35 | 36 | data class JccGlobalSettingsState( 37 | var isFoldJavaFragments: Boolean = true, 38 | var isFoldLookaheads: Boolean = true, 39 | var isFoldTokenRefs: Boolean = true, 40 | 41 | var isFoldOptions: Boolean = true, 42 | var isFoldParserDecl: Boolean = true, 43 | var isFoldTokenMgrDecl: Boolean = true, 44 | var isFoldTokenProds: Boolean = true, 45 | 46 | var isFoldBgenSections: Boolean = true 47 | ) { 48 | 49 | fun setAllFoldingOptsTo(b: Boolean) { 50 | isFoldJavaFragments = b 51 | isFoldLookaheads = b 52 | isFoldTokenRefs = b 53 | 54 | isFoldOptions = b 55 | isFoldParserDecl = b 56 | isFoldTokenMgrDecl = b 57 | isFoldTokenProds = b 58 | 59 | isFoldBgenSections = b 60 | } 61 | 62 | } 63 | -------------------------------------------------------------------------------- /src/main/kotlin/com/github/oowekyala/ijcc/ide/quickdoc/JccLexicalStateDocMaker.kt: -------------------------------------------------------------------------------- 1 | package com.github.oowekyala.ijcc.ide.quickdoc 2 | 3 | import com.github.oowekyala.ijcc.ide.quickdoc.HtmlUtil.bold 4 | import com.github.oowekyala.ijcc.ide.quickdoc.HtmlUtil.psiLink 5 | import com.github.oowekyala.ijcc.ide.quickdoc.JccDocUtil.buildQuickDoc 6 | import com.github.oowekyala.ijcc.lang.model.LexicalState 7 | import org.jetbrains.annotations.TestOnly 8 | 9 | /** 10 | * @author Clément Fournier 11 | * @since 1.2 12 | */ 13 | object JccLexicalStateDocMaker { 14 | 15 | fun makeDoc(lexicalState: LexicalState): String = 16 | makeDocImpl( 17 | lexicalStateName = lexicalState.name, 18 | successors = lexicalState.successors.map { it.name }, 19 | predecessors = lexicalState.predecessors.map { it.name }, 20 | numTokens = lexicalState.tokens.size 21 | ) 22 | 23 | @TestOnly 24 | fun makeDocImpl(lexicalStateName: String, 25 | successors: List, 26 | predecessors: List, 27 | numTokens: Int) = buildQuickDoc { 28 | definition { 29 | "Lexical state " + bold(lexicalStateName) 30 | } 31 | 32 | sections { 33 | buildSection("Successors") { 34 | successors.joinTo(this) { 35 | psiLink( 36 | linkTextUnescaped = it, 37 | linkTarget = JccDocUtil.linkRefToLexicalState(it) 38 | ) 39 | } 40 | } 41 | buildSection("Predecessors") { 42 | predecessors.joinTo(this) { 43 | psiLink( 44 | linkTextUnescaped = it, 45 | linkTarget = JccDocUtil.linkRefToLexicalState(it) 46 | ) 47 | } 48 | } 49 | 50 | section("Size in tokens") { numTokens.toString() } 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/main/kotlin/com/github/oowekyala/ijcc/lang/model/IGrammarOptions.kt: -------------------------------------------------------------------------------- 1 | package com.github.oowekyala.ijcc.lang.model 2 | 3 | import com.github.oowekyala.ijcc.lang.psi.JccFile 4 | import com.github.oowekyala.ijcc.lang.psi.JccOptionBinding 5 | 6 | /** 7 | * Most general options interface. 8 | * Handles JJTricks option files. 9 | */ 10 | interface IGrammarOptions { 11 | val nodePackage: String 12 | val isDefaultVoid: Boolean 13 | val nodePrefix: String 14 | val isTrackTokens: Boolean 15 | val nodeTakesParserArg: Boolean 16 | val isUserTokenManager: Boolean 17 | 18 | /** 19 | * If unknown, defaults to the empty string 20 | */ 21 | val grammarName: String? 22 | 23 | /** 24 | * Fallback on the options specified in the grammar file. 25 | */ 26 | val inlineBindings: InlineGrammarOptions 27 | } 28 | 29 | fun IGrammarOptions.addNodePackage(simpleName: String) = 30 | nodePackage.addPackage(simpleName) 31 | 32 | fun IGrammarOptions.addParserPackage(simpleName: String) = 33 | parserPackage.addPackage(simpleName) 34 | 35 | private fun String.addPackage(toQualify:String)= 36 | if (isEmpty()) toQualify else "$this.$toQualify" 37 | 38 | 39 | val JccFile.allOptionsBindings: List get() = options?.optionBindingList ?: emptyList() 40 | 41 | 42 | val IGrammarOptions.parserQualifiedName: String 43 | get() { 44 | val pack = parserPackage 45 | return if (pack.isEmpty()) parserSimpleName 46 | else "$pack.$parserSimpleName" 47 | } 48 | 49 | 50 | val IGrammarOptions.parserSimpleName: String 51 | get() = 52 | inlineBindings.file.parserDeclaration?.text?.let { classRegex.find(it) }?.groups?.get(1)?.value ?: "" 53 | 54 | private val packageRegex = Regex("\\bpackage\\s+([.\\w]+)") 55 | private val classRegex = Regex("\\bclass\\s+(\\w+)") 56 | 57 | val IGrammarOptions.parserPackage: String 58 | get() = inlineBindings.file.parserDeclaration?.text?.let { packageRegex.find(it) }?.groups?.get(1)?.value ?: "" 59 | -------------------------------------------------------------------------------- /src/main/kotlin/com/github/oowekyala/ijcc/ide/refs/JjtNodePolyReference.kt: -------------------------------------------------------------------------------- 1 | package com.github.oowekyala.ijcc.ide.refs 2 | 3 | import com.github.oowekyala.ijcc.lang.psi.* 4 | import com.github.oowekyala.ijcc.lang.psi.manipulators.JccIdentifierManipulator 5 | import com.intellij.openapi.util.TextRange 6 | import com.intellij.psi.PsiElement 7 | import com.intellij.psi.PsiPolyVariantReferenceBase 8 | 9 | 10 | /** 11 | * Poly reference to the multiple declarations of a JJTree node. 12 | * 13 | * @author Clément Fournier 14 | * @since 1.0 15 | */ 16 | class JjtNodePolyReference(psiElement: JjtNodeClassOwner) 17 | : PsiPolyVariantReferenceBase(psiElement) { 18 | 19 | override fun isReferenceTo(otherElt: PsiElement): Boolean = 20 | otherElt is JjtNodeClassOwner 21 | && otherElt.containingFile === element.containingFile 22 | && otherElt.isNotVoid 23 | && otherElt.nodeRawName == element.nodeRawName 24 | 25 | /** 26 | * Like [multiResolve] but doesn't wrap the results in [PsiEltResolveResult] 27 | * and an array. Returned elements always have a name. 28 | */ 29 | fun lightMultiResolve(): List = 30 | element.nodeRawName?.let { 31 | element.containingFile.getJjtreeDeclsForRawName(it) 32 | }.orEmpty() 33 | 34 | override fun multiResolve(incompleteCode: Boolean): Array> = 35 | lightMultiResolve() 36 | .map { PsiEltResolveResult(it) } 37 | .toList() 38 | .toTypedArray() 39 | 40 | override fun getRangeInElement(): TextRange = element.nodeIdentifier!!.textRange.relativize(element.textRange)!! 41 | 42 | override fun handleElementRename(newElementName: String): PsiElement = 43 | JccIdentifierManipulator().handleContentChange(element.nodeIdentifier!!, newElementName)!! 44 | 45 | override fun getVariants(): Array = 46 | JccRefVariantService.getInstance(element.project).jjtreeNodeVariants(this) 47 | 48 | } 49 | 50 | -------------------------------------------------------------------------------- /src/main/kotlin/com/github/oowekyala/ijcc/ide/gutter/JjtNodeToGrammarLineMarkerProvider.kt: -------------------------------------------------------------------------------- 1 | package com.github.oowekyala.ijcc.ide.gutter 2 | 3 | import com.github.oowekyala.ijcc.icons.JccIcons 4 | import com.github.oowekyala.ijcc.lang.model.GrammarNature 5 | import com.github.oowekyala.ijcc.lang.psi.stubs.indices.JjtreeQNameStubIndex 6 | import com.intellij.codeInsight.daemon.RelatedItemLineMarkerInfo 7 | import com.intellij.codeInsight.navigation.NavigationGutterIconBuilder 8 | import com.intellij.psi.PsiClass 9 | import com.intellij.psi.PsiElement 10 | import com.intellij.psi.search.GlobalSearchScope 11 | 12 | /** 13 | * @author Clément Fournier 14 | * @since 1.2 15 | */ 16 | class JjtNodeToGrammarLineMarkerProvider : BaseTargetingLineMarkerProvider(PsiClass::class.java) { 17 | override fun processElt(elt: PsiClass): Sequence> = 18 | elt.qualifiedName 19 | ?.let { qname -> 20 | JjtreeQNameStubIndex.get(qname, elt.project, GlobalSearchScope.allScope(elt.project)) 21 | } 22 | ?.takeIf { it.isNotEmpty() } 23 | ?.groupBy { it.containingFile } 24 | ?.mapValues { (grammar, nodes) -> 25 | 26 | if (grammar.grammarNature < GrammarNature.JJTREE) null 27 | else { 28 | elt.nameIdentifier?.let { ident -> 29 | NavigationGutterIconBuilder.create(JccIcons.GUTTER_NAVIGATE_TO_JJTREE_NODE) 30 | .setTargets(nodes) 31 | .setCellRenderer { JjtPartialDeclCellRenderer } 32 | .setTooltipText("Navigate to JJTree node in ${grammar.name}") 33 | .setPopupTitle("Select partial declaration in ${grammar.name}") 34 | .createLineMarkerInfo(ident) 35 | } 36 | } 37 | 38 | } 39 | ?.values 40 | ?.asSequence() 41 | ?.filterNotNull() 42 | .orEmpty() 43 | 44 | 45 | } 46 | -------------------------------------------------------------------------------- /src/test/kotlin/com/github/oowekyala/ijcc/ide/inspections/UnnecessaryAngledBracesRegexInspectionTest.kt: -------------------------------------------------------------------------------- 1 | package com.github.oowekyala.ijcc.ide.inspections 2 | 3 | /** 4 | * @author Clément Fournier 5 | * @since 1.0 6 | */ 7 | class UnnecessaryAngledBracesRegexInspectionTest : JccInspectionTestBase(UnnecessaryAngledBracesRegexInspection()) { 8 | 9 | 10 | private fun warnung(s: String) = warningAnnot(s, UnnecessaryAngledBracesRegexInspection.ProblemDescription) 11 | 12 | fun testLiteralString() = checkByText( 13 | warnung("""< "foo" >""").inExpansionCtx() 14 | ) 15 | 16 | fun testReferenceString() = checkByText( 17 | """ 18 | $DummyHeader 19 | 20 | TOKEN: { 21 | < FOO: "foo" > 22 | } 23 | 24 | void Foo() :{}{ 25 | ${warnung("< >")} 26 | } 27 | 28 | """.trimIndent() 29 | ) 30 | 31 | fun testParen() = checkByText( 32 | """< ("foo") > """.inExpansionCtx() 33 | ) 34 | 35 | fun testPrivateReference() = checkByText( 36 | """ 37 | $DummyHeader 38 | 39 | TOKEN: { 40 | < #FOO: "foo" > 41 | } 42 | 43 | void Foo():{} { 44 | < > 45 | } 46 | 47 | """.trimIndent() 48 | ) 49 | 50 | 51 | fun testUnclosedBraces() = checkByText( 52 | """ 53 | $DummyHeader 54 | 55 | TOKEN: { 56 | < #FOO: "foo" > 57 | } 58 | 59 | void Foo():{} { 60 | < "foo" 61 | } 62 | 63 | """.trimIndent() 64 | ) 65 | 66 | fun testUnclosedBraces2() = checkByText( 67 | """ 68 | $DummyHeader 69 | 70 | TOKEN: { 71 | < #FOO: "foo" > 72 | } 73 | 74 | void Foo():{} { 75 | < 76 | } 77 | 78 | """.trimIndent() 79 | ) 80 | 81 | 82 | } -------------------------------------------------------------------------------- /src/test/kotlin/com/github/oowekyala/ijcc/ide/inspections/UnusedProductionInspectionTest.kt: -------------------------------------------------------------------------------- 1 | package com.github.oowekyala.ijcc.ide.inspections 2 | 3 | import com.github.oowekyala.ijcc.ide.inspections.UnusedProductionInspection.Companion.ErrorType.UNREACHABLE 4 | import com.github.oowekyala.ijcc.ide.inspections.UnusedProductionInspection.Companion.ErrorType.UNUSED 5 | 6 | /** 7 | * @author Clément Fournier 8 | * @since 1.1 9 | */ 10 | class UnusedProductionInspectionTest : JccInspectionTestBase(UnusedProductionInspection()) { 11 | 12 | 13 | private fun unused(name: String) = warningAnnot(name, UNUSED.makeMessage(name)) 14 | private fun unreachable(name: String) = warningAnnot(name, UNREACHABLE.makeMessage(name)) 15 | 16 | 17 | fun `test unused`() = checkByText( 18 | """ 19 | $DummyHeader 20 | 21 | void Foo(): 22 | {} 23 | { 24 | "foo" 25 | } 26 | 27 | void ${unused("Unused")}(): {}{ 28 | "hello" 29 | } 30 | 31 | """.trimIndent() 32 | ) 33 | 34 | 35 | fun `test unreachable`() = checkByText( 36 | """ 37 | $DummyHeader 38 | 39 | TOKEN: { 40 | "foo" 41 | } 42 | 43 | void Foo(): 44 | {} 45 | { 46 | "foo" 47 | } 48 | 49 | void ${unused("Unused")}(): {}{ 50 | "hello" Unreachable() 51 | } 52 | 53 | void ${unreachable("Unreachable")}(): {}{ 54 | "bazouli" 55 | } 56 | 57 | """.trimIndent() 58 | ) 59 | 60 | 61 | fun `test suppression transitivity`() = checkByText( 62 | """ 63 | $DummyHeader 64 | 65 | TOKEN: { 66 | "foo" 67 | } 68 | 69 | void Foo(): 70 | {} 71 | { 72 | "foo" 73 | } 74 | 75 | //noinspection UnusedProduction 76 | void Unused(): {}{ 77 | "hello" Unreachable() 78 | } 79 | 80 | void Unreachable(): {}{ 81 | "bazouli" 82 | } 83 | 84 | """.trimIndent() 85 | ) 86 | 87 | 88 | } 89 | -------------------------------------------------------------------------------- /src/main/kotlin/com/github/oowekyala/ijcc/lang/psi/JccJjtreeNodeDescriptor.kt: -------------------------------------------------------------------------------- 1 | package com.github.oowekyala.ijcc.lang.psi 2 | 3 | import com.github.oowekyala.ijcc.lang.JccTypes 4 | import com.intellij.psi.PsiElement 5 | 6 | /** 7 | * Node descriptor. 8 | * 9 | * 10 | * JjtNodeDescriptor ::= "#" ( [JccIdentifier] | "void" ) [ "(" [JccJjtreeNodeDescriptorExpr] ")" ] 11 | * 12 | * @author Clément Fournier 13 | * @since 1.0 14 | */ 15 | interface JccJjtreeNodeDescriptor : JccPsiElement, JccIdentifierOwner { 16 | 17 | /** 18 | * Returns the expression if one was specified. 19 | */ 20 | val descriptorExpr: JccJjtreeNodeDescriptorExpr? 21 | get() = lastChildNoWhitespace as JccJjtreeNodeDescriptorExpr? 22 | 23 | /** Is null if this is void. */ 24 | override fun getNameIdentifier(): JccIdentifier? 25 | 26 | /** Either the identifier or the void element. */ 27 | val namingLeaf: PsiElement 28 | get() = nameIdentifier ?: node.findChildByType(JccTypes.JCC_VOID_KEYWORD)!!.psi 29 | 30 | /** 31 | * Gets the production header of the production to which this 32 | * descriptor is applied. If null then [expansionUnit] won't 33 | * return null, bc this is applied to a single expansion unit. 34 | */ 35 | val productionHeader: JccJavaNonTerminalProductionHeader? 36 | get() = parent.let { it as? JccNonTerminalProduction }?.header 37 | 38 | /** 39 | * Gets the expansion unit that is the scope of this node. 40 | * If null then [productionHeader] won't return null, bc this 41 | * is applied to a whole production. 42 | */ 43 | val expansionUnit: JccExpansionUnit? 44 | get() = parent.let { it as? JccScopedExpansionUnit }?.expansionUnit 45 | 46 | /** 47 | * Returns true if this is a "#void" annotation. 48 | */ 49 | val isVoid: Boolean 50 | get() = nameIdentifier == null 51 | 52 | val isIndefinite: Boolean 53 | get() = descriptorExpr == null 54 | 55 | val isGreaterThan: Boolean 56 | get() = descriptorExpr != null && descriptorExpr!!.isGtExpression 57 | } 58 | -------------------------------------------------------------------------------- /src/main/kotlin/com/github/oowekyala/ijcc/ide/inspections/EmptyParserActionsInspection.kt: -------------------------------------------------------------------------------- 1 | package com.github.oowekyala.ijcc.ide.inspections 2 | 3 | import com.github.oowekyala.ijcc.ide.intentions.DeleteExpansionIntention 4 | import com.github.oowekyala.ijcc.lang.cfa.isNextStep 5 | import com.github.oowekyala.ijcc.lang.psi.* 6 | import com.github.oowekyala.ijcc.util.deleteWhitespace 7 | import com.intellij.codeInspection.ProblemsHolder 8 | import com.intellij.psi.PsiElementVisitor 9 | import org.intellij.lang.annotations.Language 10 | 11 | /** 12 | * @author Clément Fournier 13 | * @since 1.0 14 | */ 15 | class EmptyParserActionsInspection : JccInspectionBase(DisplayName) { 16 | 17 | @Language("HTML") 18 | override fun getStaticDescription() = """ 19 | Reports empty parser actions unit, which are unnecessary and 20 | may cause some JavaCC warnings. 21 | """.trimIndent() 22 | 23 | 24 | override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor = 25 | object : JccVisitor() { 26 | override fun visitParserActionsUnit(o: JccParserActionsUnit) { 27 | 28 | if (o.text.deleteWhitespace() == "{}") { 29 | 30 | val parentScope = 31 | o.ancestors(includeSelf = false) 32 | .filterIsInstance() 33 | .firstOrNull { it.isNotVoid } 34 | 35 | if (parentScope != null && o.isNextStep(parentScope) && o.prevSiblingNoWhitespace is JccParserActionsUnit) 36 | return 37 | 38 | holder.registerProblem( 39 | o, 40 | ProblemDescription, 41 | DeleteExpansionIntention.quickFix(FixDescription, o.containingFile) 42 | ) 43 | } 44 | } 45 | } 46 | 47 | 48 | companion object { 49 | const val DisplayName = "Empty parser actions unit" 50 | const val ProblemDescription = "Empty parser actions unit" 51 | const val FixDescription = "Delete expansion unit" 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /do-release.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Release script for the idea plugin 4 | # Execute it from the root directory 5 | 6 | set -e 7 | 8 | 9 | git checkout main -q 10 | 11 | CHANGELOG_LOCATION="changelog.html" 12 | 13 | function incr_ver_num() { 14 | ret=$(echo "$1" | awk -F. -v OFS=. 'NF==1{print ++$NF}; NF>1{if(length($NF+1)>length($NF))$(NF-1)++; $NF=sprintf("%0*d", length($NF), ($NF+1)%(10^length($NF))); print}') 15 | echo "$ret" 16 | } 17 | 18 | release_version=$(./gradlew properties -q --console=plain | grep "version:" | awk '{print $2}') 19 | 20 | echo "Preparing release for version $release_version, from branch main..." 21 | 22 | read -p "Enter the name of the release tag (default v$release_version): " tagname 23 | 24 | if [[ -z "$tagname" ]]; then 25 | tagname="v$release_version" 26 | fi 27 | 28 | git tag -fa "$tagname" -F "$CHANGELOG_LOCATION" 29 | 30 | release_changelog=$(mktemp) 31 | 32 | cp "$CHANGELOG_LOCATION" "$release_changelog" 33 | 34 | echo "Publishing plugin to repository..." 35 | 36 | ./gradlew publishPlugin 37 | 38 | 39 | echo "Pushing objects..." 40 | 41 | git push origin main 42 | git push --tags 43 | 44 | default_nr=$(incr_ver_num "$release_version") 45 | 46 | read -p "What's the version number of the next release? (default $default_nr)" next_release 47 | 48 | if [[ -z "$next_release" ]]; then 49 | next_release="$default_nr" 50 | fi 51 | 52 | replacement="s/version = \"$release_version\"/version = \"$next_release\"/" 53 | 54 | sed -i -e "$replacement" build.gradle.kts 55 | git add build.gradle.kts 56 | 57 | echo "\nResetting the changelog..." 58 | 59 | read -r -d '' DEFAULT_CHANGELOG <<'EOF' 60 |

What's new: 61 | 62 |

65 | 66 |

What's changed: 67 |

    68 |
  • TODO
  • 69 |
70 | 71 |

What's fixed: 72 |

    73 |
  • TODO
  • 74 |
75 | EOF 76 | 77 | echo "$DEFAULT_CHANGELOG" > "$CHANGELOG_LOCATION" 78 | 79 | git add "$CHANGELOG_LOCATION" 80 | git commit -m "Prepare next development version $next_release" 81 | 82 | echo "\nSuccessfully released $release_version, edit the release notes on Github with the contents of $release_changelog" 83 | -------------------------------------------------------------------------------- /src/main/kotlin/com/github/oowekyala/ijcc/ide/refs/JccBnfStringLiteralReference.kt: -------------------------------------------------------------------------------- 1 | package com.github.oowekyala.ijcc.ide.refs 2 | 3 | import com.github.oowekyala.ijcc.lang.model.RegexKind 4 | import com.github.oowekyala.ijcc.lang.model.Token 5 | import com.github.oowekyala.ijcc.lang.psi.JccLiteralRegexUnit 6 | import com.github.oowekyala.ijcc.lang.psi.JccRegexExpansionUnit 7 | import com.github.oowekyala.ijcc.lang.psi.JccRegexSpec 8 | import com.github.oowekyala.ijcc.lang.psi.innerRange 9 | import com.intellij.openapi.util.TextRange 10 | import com.intellij.psi.PsiElement 11 | import com.intellij.psi.PsiReferenceBase 12 | 13 | /** 14 | * Reference from a literal regex written in BNF to its [Token]. Regexes in BNF are always in the 15 | * default state. 16 | * 17 | * If there exists a [JccRegexSpec] in the default state defined as *exactly* this string, 18 | * the reference points to that regex. Otherwise a new string token is synthesized by JavaCC 19 | * (see documentation on [Token]). Then this reference's [resolve] points to the [JccRegexExpansionUnit] 20 | * generating it. 21 | * 22 | * @author Clément Fournier 23 | * @since 1.0 24 | */ 25 | class JccBnfStringLiteralReference(element: JccLiteralRegexUnit) : 26 | PsiReferenceBase(element) { 27 | 28 | /** 29 | * Resolves the token that matches this literal. If [exact], only exact 30 | * string literals will be returned (meaning [Token.asStringToken] is non-null). 31 | * Otherwise full regex will be used. 32 | */ 33 | fun resolveToken(exact: Boolean): Token? = 34 | element.containingFile 35 | .lexicalGrammar 36 | .defaultState 37 | .matchLiteral(element, exact, RegexKind.All) 38 | 39 | override fun resolve(): PsiElement? = resolveToken(exact = true)?.psiElement 40 | 41 | override fun calculateDefaultRangeInElement(): TextRange = element.innerRange() 42 | 43 | /** 44 | * Enables autocompletion. Only tokens from the default state are considered. 45 | */ 46 | override fun getVariants(): Array = 47 | JccRefVariantService.getInstance(element.project).stringLiteralVariants(this) 48 | 49 | } 50 | --------------------------------------------------------------------------------