├── .github ├── PULL_REQUEST_TEMPLATE.md ├── dependabot.yml └── workflows │ ├── gradle.yml │ └── release.yml ├── .gitignore ├── ARCHITECTURE.md ├── LICENSE ├── README.md ├── build.gradle ├── developer-cert-of-origin.txt ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── historical-contributors-agreement.txt ├── images ├── ambig-right-click.png ├── ambig1.png ├── ambig2.png ├── color-prefs.png ├── completion.png ├── def-literals.png ├── error-popup.png ├── findusages.png ├── java-grammar.png ├── lexer-templates.png ├── live-preview-error.png ├── live-preview.png ├── lookahead1.png ├── parse-region.png ├── per-file-config.png ├── profiler.png ├── pycharm.png ├── token-tooltips.png ├── tool-console.png └── unicode.png ├── intellij-plugin-v4.iml ├── scripts └── github_release_notes.py └── src ├── main ├── antlr │ └── org │ │ └── antlr │ │ └── intellij │ │ └── plugin │ │ └── parser │ │ ├── ANTLRv4Lexer.g4 │ │ ├── ANTLRv4Parser.g4 │ │ └── LexBasic.g4 ├── java │ └── org │ │ └── antlr │ │ └── intellij │ │ ├── adaptor │ │ └── parser │ │ │ └── PsiElementFactory.java │ │ └── plugin │ │ ├── ANTLRv4ASTFactory.java │ │ ├── ANTLRv4Commenter.java │ │ ├── ANTLRv4ExternalAnnotator.java │ │ ├── ANTLRv4FileRoot.java │ │ ├── ANTLRv4FileType.java │ │ ├── ANTLRv4FileTypeFactory.java │ │ ├── ANTLRv4FindUsagesProvider.java │ │ ├── ANTLRv4IconProvider.java │ │ ├── ANTLRv4Language.java │ │ ├── ANTLRv4ParserDefinition.java │ │ ├── ANTLRv4PluginController.java │ │ ├── ANTLRv4SyntaxHighlighter.java │ │ ├── ANTLRv4SyntaxHighlighterFactory.java │ │ ├── ANTLRv4TokenTypes.java │ │ ├── Icons.java │ │ ├── PluginIgnoreMissingTokensFileErrorManager.java │ │ ├── Utils.java │ │ ├── actions │ │ ├── AnnotationIntentActionsFactory.java │ │ ├── ChooseExtractedRuleName.java │ │ ├── ConfigureANTLRAction.java │ │ ├── ExtractRuleAction.java │ │ ├── GenerateLexerRulesForLiteralsAction.java │ │ ├── GenerateParserAction.java │ │ ├── InlineRuleAction.java │ │ ├── MyActionUtils.java │ │ ├── TestRuleAction.java │ │ └── UniquifyRuleRefs.java │ │ ├── adaptors │ │ ├── ANTLRv4GrammarParser.java │ │ ├── ANTLRv4LexerAdaptor.java │ │ └── ANTLRv4LexerState.java │ │ ├── configdialogs │ │ ├── ANTLRv4ColorsPage.java │ │ ├── ANTLRv4GrammarProperties.java │ │ ├── ANTLRv4GrammarPropertiesComponent.java │ │ ├── ANTLRv4GrammarPropertiesStore.java │ │ ├── ANTLRv4ProjectSettings.java │ │ ├── CaseChangingStrategyConverter.java │ │ ├── ConfigANTLRDialogPanel.form │ │ └── ConfigANTLRPerGrammar.java │ │ ├── editor │ │ └── ANTLRv4BraceMatcher.java │ │ ├── folding │ │ ├── ANTLRv4FoldingBuilder.java │ │ └── ANTLRv4FoldingSettings.java │ │ ├── generators │ │ ├── LiteralChooser.java │ │ ├── LiteralChooserObject.java │ │ └── LiteralChooserRenderer.java │ │ ├── parsing │ │ ├── CaseChangingCharStream.java │ │ ├── CaseChangingStrategy.java │ │ ├── LexerWatchdog.java │ │ ├── LoadGrammarsToolListener.java │ │ ├── ParsingResult.java │ │ ├── ParsingUtils.java │ │ ├── PreviewInterpreterRuleContext.java │ │ ├── PreviewParser.java │ │ ├── RunANTLRListener.java │ │ ├── RunANTLROnGrammarFile.java │ │ └── TokenStreamSubset.java │ │ ├── preview │ │ ├── AltLabelTextProvider.java │ │ ├── CancelParserAction.java │ │ ├── HierarchyViewer.java │ │ ├── InputPanel.form │ │ ├── InputPanel.java │ │ ├── ParseTreeContextualMenu.java │ │ ├── ParsingResultSelectionListener.java │ │ ├── PreviewEditorMouseListener.java │ │ ├── PreviewPanel.java │ │ ├── PreviewState.java │ │ ├── ShowAmbigTreesDialog.form │ │ ├── ShowAmbigTreesDialog.java │ │ ├── TrackpadZoomingTreeView.java │ │ ├── UberTreeViewer.java │ │ └── WrappedFlowLayout.java │ │ ├── profiler │ │ ├── ExpertProfilerTableDataModel.java │ │ ├── ProfilerPanel.form │ │ ├── ProfilerPanel.java │ │ ├── ProfilerTableDataModel.java │ │ └── SimpleProfilerTableDataModel.java │ │ ├── psi │ │ ├── ANTLRv4IndexPatternBuilder.java │ │ ├── AtAction.java │ │ ├── ChannelSpecNode.java │ │ ├── GrammarElementRef.java │ │ ├── GrammarElementRefNode.java │ │ ├── GrammarSpecNode.java │ │ ├── LexerRuleRefNode.java │ │ ├── LexerRuleSpecNode.java │ │ ├── ModeSpecNode.java │ │ ├── MyPsiUtils.java │ │ ├── ParserRuleRefNode.java │ │ ├── ParserRuleSpecNode.java │ │ ├── RuleSpecNode.java │ │ ├── RulesNode.java │ │ ├── StringLiteralElement.java │ │ ├── StringLiteralRef.java │ │ └── TokenSpecNode.java │ │ ├── refactor │ │ ├── ANTLRv4RefactoringSupport.java │ │ └── RefactorUtils.java │ │ ├── resolve │ │ ├── ImportResolver.java │ │ └── TokenVocabResolver.java │ │ ├── structview │ │ ├── ANTLRv4ItemPresentation.java │ │ ├── ANTLRv4StructureViewElement.java │ │ ├── ANTLRv4StructureViewFactory.java │ │ └── ANTLRv4StructureViewModel.java │ │ ├── templates │ │ ├── ANTLRGenericContext.java │ │ ├── ANTLRLiveTemplateContext.java │ │ ├── ANTLRLiveTemplatesProvider.java │ │ └── OutsideRuleContext.java │ │ └── validation │ │ ├── AddTokenDefinitionFix.java │ │ ├── CreateRuleFix.java │ │ ├── GrammarInfoMessage.java │ │ ├── GrammarIssue.java │ │ ├── GrammarIssuesCollector.java │ │ └── GrammarIssuesCollectorToolListener.java └── resources │ ├── META-INF │ ├── plugin.xml │ └── pluginIcon.svg │ ├── colorSchemes │ ├── ANTLRv4Darcula.xml │ └── ANTLRv4Default.xml │ ├── icons │ └── org │ │ └── antlr │ │ └── intellij │ │ └── plugin │ │ ├── antlr-icon.idraw │ │ ├── antlr-tiny-icon.idraw │ │ ├── antlr.png │ │ ├── antlr.svg │ │ ├── antlr@2x.png │ │ ├── icons.graffle │ │ ├── lexer-rule.png │ │ ├── lexer-rule.svg │ │ ├── lexer-rule@2x.png │ │ ├── mode.png │ │ ├── mode.svg │ │ ├── mode@2x.png │ │ ├── parser-rule.png │ │ ├── parser-rule.svg │ │ ├── parser-rule@2x.png │ │ ├── toolWindowAntlr.svg │ │ └── toolWindowAntlr_dark.svg │ ├── liveTemplates │ └── lexer │ │ └── user.xml │ └── templates │ └── org │ └── antlr │ └── intellij │ └── plugin │ └── gen │ └── Java.stg └── test ├── java └── org │ └── antlr │ └── intellij │ └── plugin │ ├── ANTLRv4ExternalAnnotatorTest.java │ ├── TestUtils.java │ ├── actions │ └── AnnotationIntentActionsFactoryTest.java │ ├── configdialogs │ ├── ANTLRv4GrammarPropertiesStoreTest.java │ ├── ANTLRv4GrammarPropertiesTest.java │ └── ConfigANTLRPerGrammarTest.java │ ├── editor │ ├── Issue540Test.java │ ├── Issue559Test.java │ └── MockToolWindow.java │ ├── folding │ └── ANTLRv4FoldingBuilderTest.java │ ├── parsing │ ├── Issue374Test.java │ ├── Issue403Test.java │ └── RunANTLROnGrammarFileTest.java │ ├── psi │ └── GrammarElementRefTest.java │ └── validation │ ├── AddTokenDefinitionFixTest.java │ └── CreateRuleFixTest.java └── resources ├── editor ├── T1.g4 └── T2.g4 ├── parser └── SqlBase.g4 ├── quickfixes └── CreateRuleFix │ └── missingRule.g4 └── references ├── FooLexer.g4 ├── FooParser.g4 ├── FooParser2.g4 ├── Modes.g4 ├── SimpleGrammar.g4 ├── SimpleGrammar2.g4 ├── imported.g4 ├── imported2.g4 ├── imported3.g4 └── importing.g4 /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | @@ -1,5 +1,23 @@ 2 | 22 | 23 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # Dependabot configuration: 2 | # https://docs.github.com/en/free-pro-team@latest/github/administering-a-repository/configuration-options-for-dependency-updates 3 | 4 | version: 2 5 | updates: 6 | - package-ecosystem: "gradle" 7 | directory: "/" 8 | schedule: 9 | interval: "daily" 10 | -------------------------------------------------------------------------------- /.github/workflows/gradle.yml: -------------------------------------------------------------------------------- 1 | # This workflow will build a Java project with Gradle 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-gradle 3 | 4 | name: Java CI 5 | 6 | on: [ push, pull_request ] 7 | 8 | jobs: 9 | build: 10 | 11 | runs-on: ubuntu-latest 12 | 13 | strategy: 14 | fail-fast: false 15 | matrix: 16 | env: 17 | # see https://www.jetbrains.com/idea/download/previous.html 18 | # and https://www.jetbrains.com/intellij-repository/snapshots/ 19 | - IDEA_VERSION: IC-2022.3 # Oldest supported version 20 | - IDEA_VERSION: IC-2023.1 21 | - IDEA_VERSION: IC-2023.3.2 22 | 23 | steps: 24 | - uses: actions/checkout@v3 25 | - name: Set up JDK 17 26 | uses: actions/setup-java@v3 27 | with: 28 | java-version: 17 29 | distribution: temurin 30 | - name: Build with Gradle 31 | run: | 32 | ./gradlew -PideaVersion=${IDEA_VERSION} check buildPlugin 33 | env: ${{ matrix.env }} 34 | - name: Archive distribution artifact 35 | uses: actions/upload-artifact@v3 36 | with: 37 | name: "antlr-intellij-development" 38 | path: build/distributions/antlr-intellij-plugin-v4-*.zip 39 | if: matrix.env.IDEA_VERSION == 'IC-2022.3' 40 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | # This workflow will build a Java project with Gradle 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-gradle 3 | 4 | name: Java CI for releases 5 | 6 | on: 7 | release: 8 | types: 9 | - published 10 | 11 | jobs: 12 | build: 13 | 14 | runs-on: ubuntu-latest 15 | 16 | strategy: 17 | matrix: 18 | env: 19 | # see https://www.jetbrains.com/idea/download/previous.html 20 | # and https://www.jetbrains.com/intellij-repository/snapshots/ 21 | - IDEA_VERSION: IC-2022.3.3 22 | SINCE_VERSION: 223 23 | UNTIL_VERSION: 230.* 24 | VERSION_SUFFIX: "-2022" 25 | - IDEA_VERSION: IC-2023.1 26 | SINCE_VERSION: 230 27 | UNTIL_VERSION: 240.* 28 | VERSION_SUFFIX: "-2023" 29 | - IDEA_VERSION: IC-2023.1 30 | SINCE_VERSION: 240 31 | VERSION_SUFFIX: 32 | 33 | steps: 34 | - uses: actions/checkout@v3 35 | - name: Set up JDK 17 36 | uses: actions/setup-java@v3 37 | with: 38 | java-version: 17 39 | distribution: temurin 40 | 41 | - name: Build with Gradle 42 | run: | 43 | ./gradlew -PideaVersion=${IDEA_VERSION} -PsinceBuildVersion=${SINCE_VERSION} -PuntilBuildVersion=${UNTIL_VERSION} -PpluginVersion=${GITHUB_REF_NAME}${VERSION_SUFFIX} check buildPlugin 44 | env: ${{ matrix.env }} 45 | 46 | - name: Archive distribution artifact 47 | uses: actions/upload-artifact@v3 48 | with: 49 | name: "antlr-intellij${{matrix.env.VERSION_SUFFIX}}" 50 | path: build/distributions/antlr-intellij-plugin-v4-*.zip 51 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Build intermediate 2 | /gen/ 3 | 4 | # Build output 5 | /out/ 6 | /build/ 7 | 8 | # downloaded libs/resources 9 | /lib/ 10 | 11 | .gradle/ 12 | 13 | *.pyc 14 | bilder.py 15 | intellij-plugin-v4.iml 16 | 17 | # Per-user settings 18 | # https://intellij-support.jetbrains.com/entries/23393067 19 | 20 | .idea/scopes/ 21 | .idea/modules/ 22 | .idea/copyright/ 23 | .idea/libraries/ 24 | .idea/codeStyles/ 25 | .idea/inspectionProfiles/ 26 | 27 | .idea/ant.xml 28 | .idea/vcs.xml 29 | .idea/misc.xml 30 | .idea/tasks.xml 31 | .idea/gradle.xml 32 | .idea/kotlinc.xml 33 | .idea/modules.xml 34 | .idea/compiler.xml 35 | .idea/encodings.xml 36 | .idea/workspace.xml 37 | .idea/uiDesigner.xml 38 | .idea/dbnavigator.xml 39 | .idea/preferred-vcs.xml 40 | .idea/codeStyleSettings.xml 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /ARCHITECTURE.md: -------------------------------------------------------------------------------- 1 | # Plugin architecture 2 | 3 | ## Build 4 | 5 | Gradle is used to build the project. [`gradle-intellij-plugin`][1] is used to pull 6 | any given version of IntelliJ to add its JARs to the classpath, build forms, 7 | publish to the marketplace etc. 8 | 9 | GitHub Actions are used for continuous integration, see files in `.github/workflows`. 10 | 11 | ## Lexing/parsing 12 | 13 | An ANTLR v4 grammar in `src/main/antlr` is used to generate a lexer/parser. 14 | `ANTLRv4ParserDefinition` uses the [`antlr4-intellij-adaptor` library][2] to 15 | delegate parsing/lexing to these generated classes. 16 | 17 | Syntax highlighting is also using the generated lexer (by delegation) in 18 | `org.antlr.intellij.plugin.ANTLRv4SyntaxHighlighter.getHighlightingLexer`. 19 | 20 | ## Error checking 21 | 22 | The plugin embeds a complete version of ANTLR v4. To highlight warnings in the 23 | editor, the file is parsed again in `ANTLRv4ExternalAnnotator`, but this time 24 | using the "official" parser. The annotator constructs a `org.antlr.v4.Tool` 25 | with all the flags configured in the `Configure ANTLR...` dialog. 26 | `GrammarIssuesCollector` sets up an error listener, then processes all the issues 27 | reported by the `Tool` to show them in the editor. 28 | 29 | ## Preview window 30 | 31 | Contrary to ANTLRWorks, the IntelliJ plugin does not run the actual generated 32 | parser to test a grammar. Instead, it uses [ANTLR interpreters][3]. This is 33 | mainly because the IDE classpath and the project classpath are totally different, 34 | meaning generated parsers and their custom code and dependencies are not 35 | available from the IDE process. 36 | 37 | The interpreter provides a convenient way to "run" grammars without needing 38 | any generated code. A major drawback of this approach is that custom code will 39 | not be executed: `@members`, actions, predicates... 40 | 41 | The entry point for the preview window is `PreviewPanel`, which holds the main 42 | Swing component displayed in the tool window. It uses `ANTLRv4PluginController` 43 | to maintain a cache of parsed grammars that can be previewed. Everytime the 44 | editor switches to another .g4 grammar, the preview is updated accordingly: 45 | the input text and selected rule are restored to their previous state (if any), 46 | and the interpreter results are updated. Interpreter results include profiling 47 | data, a list of tokens and a tree of matched rules and terminal nodes (both 48 | in graphical form and in JTree form). 49 | 50 | `PreviewPanel` acts as an orchestrator between inputs (grammar changed event, 51 | input text changed event) and outputs (profiler, token list, parse tree etc.). 52 | 53 | Since the preview panel has full control over the interpreter, it can monitor 54 | the parsing phase to detect potential infinite loops caused by bad grammars, 55 | thus preventing IDE freezes or OutOfMemoryExceptions. 56 | 57 | [1]: https://github.com/JetBrains/gradle-intellij-plugin 58 | [2]: https://github.com/antlr/antlr4-intellij-adaptor/ 59 | [3]: https://github.com/antlr/antlr4/blob/master/doc/interpreters.md 60 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013, Terence Parr 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, 5 | are permitted provided that the following conditions are met: 6 | 7 | Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | Redistributions in binary form must reproduce the above copyright notice, this 11 | list of conditions and the following disclaimer in the documentation and/or 12 | other materials provided with the distribution. 13 | 14 | Neither the name of the {organization} nor the names of its 15 | contributors may be used to endorse or promote products derived from 16 | this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 19 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 22 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 25 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 27 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | repositories { 3 | mavenCentral() 4 | } 5 | } 6 | 7 | plugins { 8 | id "org.jetbrains.intellij" version "1.17.4" 9 | } 10 | 11 | wrapper { 12 | gradleVersion = '7.5' 13 | } 14 | 15 | group 'antlr' 16 | version pluginVersion 17 | 18 | apply plugin: 'java' 19 | apply plugin: 'org.jetbrains.intellij' 20 | apply plugin: 'antlr' 21 | 22 | compileJava { 23 | sourceCompatibility = '17' 24 | targetCompatibility = '17' 25 | } 26 | 27 | intellij { 28 | version = ideaVersion 29 | 30 | pluginName = 'antlr-intellij-plugin-v4' 31 | downloadSources = true 32 | updateSinceUntilBuild = true 33 | 34 | patchPluginXml { 35 | sinceBuild = sinceBuildVersion 36 | untilBuild = untilBuildVersion 37 | } 38 | } 39 | 40 | repositories { 41 | mavenCentral() 42 | mavenLocal() 43 | maven { // Gets snapshots of antlr if needed 44 | url "https://oss.sonatype.org/content/repositories/snapshots/" 45 | } 46 | } 47 | 48 | dependencies { 49 | antlr("org.antlr:antlr4:$antlr4Version") { // use ANTLR version 4 50 | exclude group:'com.ibm.icu', module:'icu4j' 51 | } 52 | implementation "org.antlr:antlr4-intellij-adaptor:0.1" 53 | implementation group: 'org.jfree', name: 'org.jfree.svg', version: '5.0.6' 54 | testImplementation group: 'junit', name: 'junit', version: '4.13.2' 55 | testImplementation group: 'org.mockito', name: 'mockito-core', version: '5.15.2' 56 | } 57 | 58 | generateGrammarSource { 59 | include("**/ANTLRv4*.g4") 60 | 61 | arguments += [ 62 | "-package", "org.antlr.intellij.plugin.parser", 63 | "-lib", "src/main/antlr/org/antlr/intellij/plugin/parser", 64 | "-Xexact-output-dir" 65 | ] 66 | } 67 | 68 | test { 69 | testLogging { 70 | events "failed" 71 | exceptionFormat "full" 72 | } 73 | } 74 | 75 | runIde { 76 | jvmArgs '-Xmx8G' 77 | } 78 | -------------------------------------------------------------------------------- /developer-cert-of-origin.txt: -------------------------------------------------------------------------------- 1 | As of 1.18, the ANTLR plugin uses the Linux Foundation's Developer 2 | Certificate of Origin, DCO, version 1.1. See either 3 | https://developercertificate.org/ or the text below. 4 | 5 | Each commit requires a "signature", which is simple as 6 | using `-s` (not `-S`) to the git commit command: 7 | 8 | git commit -s -m 'This is my commit message' 9 | 10 | Github's pull request process enforces the sig and gives 11 | instructions on how to fix any commits that lack the sig. 12 | See https://github.com/apps/dco for more info. 13 | 14 | No signature is required in this file (unlike the 15 | previous contributor's certificate of origin.) 16 | 17 | ----- https://developercertificate.org/ ------ 18 | 19 | Developer Certificate of Origin 20 | Version 1.1 21 | 22 | Copyright (C) 2004, 2006 The Linux Foundation and its contributors. 23 | 24 | Everyone is permitted to copy and distribute verbatim copies of this 25 | license document, but changing it is not allowed. 26 | 27 | 28 | Developer's Certificate of Origin 1.1 29 | 30 | By making a contribution to this project, I certify that: 31 | 32 | (a) The contribution was created in whole or in part by me and I 33 | have the right to submit it under the open source license 34 | indicated in the file; or 35 | 36 | (b) The contribution is based upon previous work that, to the best 37 | of my knowledge, is covered under an appropriate open source 38 | license and I have the right under that license to submit that 39 | work with modifications, whether created in whole or in part 40 | by me, under the same open source license (unless I am 41 | permitted to submit under a different license), as indicated 42 | in the file; or 43 | 44 | (c) The contribution was provided directly to me by some other 45 | person who certified (a), (b) or (c) and I have not modified 46 | it. 47 | 48 | (d) I understand and agree that this project and the contribution 49 | are public and that a record of the contribution (including all 50 | personal information I submit with it, including my sign-off) is 51 | maintained indefinitely and may be redistributed consistent with 52 | this project or the open source license(s) involved. 53 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Overridden by Gradle during releases 2 | pluginVersion=1.24 3 | 4 | # e.g. IC-2016.3.3, IU-2018.2.5 etc 5 | # For a list of possible values, refer to the section 'com.jetbrains.intellij.idea' at 6 | # https://www.jetbrains.com/idea/download/other.html 7 | 8 | # Oldest version currently supported 9 | ideaVersion=IC-2022.3 10 | 11 | #ideaVersion=IC-2023.1 12 | 13 | # The version of ANTLR v4 that will be used to generate the parser 14 | antlr4Version=4.13.2 15 | 16 | # Lower platform version supported by this build. Leave empty to keep the default value in plugin.xml. 17 | sinceBuildVersion= 18 | # Upper platform version supported by this build. Leave empty to support latest versions. 19 | untilBuildVersion= 20 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antlr/intellij-plugin-v4/46bc9a5d2bdd88d0d2144e5cd26f15ba8bf409d2/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%"=="" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%"=="" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if %ERRORLEVEL% equ 0 goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if %ERRORLEVEL% equ 0 goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | set EXIT_CODE=%ERRORLEVEL% 84 | if %EXIT_CODE% equ 0 set EXIT_CODE=1 85 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 86 | exit /b %EXIT_CODE% 87 | 88 | :mainEnd 89 | if "%OS%"=="Windows_NT" endlocal 90 | 91 | :omega 92 | -------------------------------------------------------------------------------- /historical-contributors-agreement.txt: -------------------------------------------------------------------------------- 1 | ANTLR Project Contributors Certification of Origin and Rights 2 | 3 | All contributors to ANTLR v4 must formally agree to abide by this 4 | certificate of origin by signing on the bottom with their github 5 | userid, full name, email address (you can obscure your e-mail, but it 6 | must be computable by human), and date. 7 | 8 | By signing this agreement, you are warranting and representing that 9 | you have the right to release code contributions or other content free 10 | of any obligations to third parties and are granting Terence Parr and 11 | ANTLR project contributors, henceforth referred to as The ANTLR 12 | Project, a license to incorporate it into The ANTLR Project tools 13 | (such as ANTLRWorks and StringTemplate) or related works under the BSD 14 | license. You understand that The ANTLR Project may or may not 15 | incorporate your contribution and you warrant and represent the 16 | following: 17 | 18 | 1. I am the creator of all my contributions. I am the author of all 19 | contributed work submitted and further warrant and represent that 20 | such work is my original creation and I have the right to license 21 | it to The ANTLR Project for release under the 3-clause BSD 22 | license. I hereby grant The ANTLR Project a nonexclusive, 23 | irrevocable, royalty-free, worldwide license to reproduce, 24 | distribute, prepare derivative works, and otherwise use this 25 | contribution as part of the ANTLR project, associated 26 | documentation, books, and tools at no cost to The ANTLR Project. 27 | 28 | 2. I have the right to submit. This submission does not violate the 29 | rights of any person or entity and that I have legal authority over 30 | this submission and to make this certification. 31 | 32 | 3. If I violate another's rights, liability lies with me. I agree to 33 | defend, indemnify, and hold The ANTLR Project and ANTLR users 34 | harmless from any claim or demand, including reasonable attorney 35 | fees, made by any third party due to or arising out of my violation 36 | of these terms and conditions or my violation of the rights of 37 | another person or entity. 38 | 39 | 4. I understand and agree that this project and the contribution are 40 | public and that a record of the contribution (including all 41 | personal information I submit with it, including my sign-off) is 42 | maintained indefinitely and may be redistributed consistent with 43 | this project or the open source license indicated in the file. 44 | 45 | I have read this agreement and do so certify by adding my signoff to 46 | the end of the following contributors list. 47 | 48 | CONTRIBUTORS: 49 | 50 | YYYY/MM/DD, github id, Full name, email 51 | 2012/07/12, parrt, Terence Parr, parrt@antlr.org 52 | 2014/01/30, sharwell, Sam Harwell, sam@tunnelvisionlabs.com 53 | 2014/04/04, migulorama, Miguel Marques, migulorama@gmail.com 54 | 2014/04/21, mhordecki, Mike Hordecki, mike@hordecki.com 55 | 2014/12/22, ponomandr, Andrey Ponomarev, ponomandr@gmail.com 56 | 2015/02/07, jasonnn, Jason Markham, jasonmarkham@gmail.com 57 | 2015/03/26, alekum, Alexey Kuzmenko, rojaster@yandex.ru(1ikb3zz@gmail.com) 58 | 2015/07/30, maccimo, Maxim Degtyarev, mdegtyarev@gmail.com 59 | 2015/08/12, bjansen, Bastien Jansen, bastien.jansen@gmx.com 60 | 2016/01/08, jwhiting, James Whiting, james.whiting@gmail.com 61 | 2018/10/24, brizjin, Ivan Bryzzhin, brizjin@gmail.com 62 | 2019/06/29, alexkli, Alexander Klimetschek, aklimets@adobe.com 63 | 2019/07/10, wojciszek, Wojciech Kruczkowski, kruczkowski.wojciech@gmail.com 64 | 2019/11/24, nopeslide, Uffke Drechsler, nopeslide@web.de 65 | 2020/12/05, roggenbrot, Sascha Dais, sdais@gmx.net 66 | 2021/11/04, OleksiiKovalov, Oleksii Kovalov, Oleksii.Kovalov@outlook.com 67 | 68 | -------------------------------------------------------------------------------- /images/ambig-right-click.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antlr/intellij-plugin-v4/46bc9a5d2bdd88d0d2144e5cd26f15ba8bf409d2/images/ambig-right-click.png -------------------------------------------------------------------------------- /images/ambig1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antlr/intellij-plugin-v4/46bc9a5d2bdd88d0d2144e5cd26f15ba8bf409d2/images/ambig1.png -------------------------------------------------------------------------------- /images/ambig2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antlr/intellij-plugin-v4/46bc9a5d2bdd88d0d2144e5cd26f15ba8bf409d2/images/ambig2.png -------------------------------------------------------------------------------- /images/color-prefs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antlr/intellij-plugin-v4/46bc9a5d2bdd88d0d2144e5cd26f15ba8bf409d2/images/color-prefs.png -------------------------------------------------------------------------------- /images/completion.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antlr/intellij-plugin-v4/46bc9a5d2bdd88d0d2144e5cd26f15ba8bf409d2/images/completion.png -------------------------------------------------------------------------------- /images/def-literals.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antlr/intellij-plugin-v4/46bc9a5d2bdd88d0d2144e5cd26f15ba8bf409d2/images/def-literals.png -------------------------------------------------------------------------------- /images/error-popup.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antlr/intellij-plugin-v4/46bc9a5d2bdd88d0d2144e5cd26f15ba8bf409d2/images/error-popup.png -------------------------------------------------------------------------------- /images/findusages.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antlr/intellij-plugin-v4/46bc9a5d2bdd88d0d2144e5cd26f15ba8bf409d2/images/findusages.png -------------------------------------------------------------------------------- /images/java-grammar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antlr/intellij-plugin-v4/46bc9a5d2bdd88d0d2144e5cd26f15ba8bf409d2/images/java-grammar.png -------------------------------------------------------------------------------- /images/lexer-templates.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antlr/intellij-plugin-v4/46bc9a5d2bdd88d0d2144e5cd26f15ba8bf409d2/images/lexer-templates.png -------------------------------------------------------------------------------- /images/live-preview-error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antlr/intellij-plugin-v4/46bc9a5d2bdd88d0d2144e5cd26f15ba8bf409d2/images/live-preview-error.png -------------------------------------------------------------------------------- /images/live-preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antlr/intellij-plugin-v4/46bc9a5d2bdd88d0d2144e5cd26f15ba8bf409d2/images/live-preview.png -------------------------------------------------------------------------------- /images/lookahead1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antlr/intellij-plugin-v4/46bc9a5d2bdd88d0d2144e5cd26f15ba8bf409d2/images/lookahead1.png -------------------------------------------------------------------------------- /images/parse-region.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antlr/intellij-plugin-v4/46bc9a5d2bdd88d0d2144e5cd26f15ba8bf409d2/images/parse-region.png -------------------------------------------------------------------------------- /images/per-file-config.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antlr/intellij-plugin-v4/46bc9a5d2bdd88d0d2144e5cd26f15ba8bf409d2/images/per-file-config.png -------------------------------------------------------------------------------- /images/profiler.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antlr/intellij-plugin-v4/46bc9a5d2bdd88d0d2144e5cd26f15ba8bf409d2/images/profiler.png -------------------------------------------------------------------------------- /images/pycharm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antlr/intellij-plugin-v4/46bc9a5d2bdd88d0d2144e5cd26f15ba8bf409d2/images/pycharm.png -------------------------------------------------------------------------------- /images/token-tooltips.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antlr/intellij-plugin-v4/46bc9a5d2bdd88d0d2144e5cd26f15ba8bf409d2/images/token-tooltips.png -------------------------------------------------------------------------------- /images/tool-console.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antlr/intellij-plugin-v4/46bc9a5d2bdd88d0d2144e5cd26f15ba8bf409d2/images/tool-console.png -------------------------------------------------------------------------------- /images/unicode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antlr/intellij-plugin-v4/46bc9a5d2bdd88d0d2144e5cd26f15ba8bf409d2/images/unicode.png -------------------------------------------------------------------------------- /intellij-plugin-v4.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 38 | -------------------------------------------------------------------------------- /scripts/github_release_notes.py: -------------------------------------------------------------------------------- 1 | # Get github issues / PR for a release 2 | # Exec with "python github_release_notes.py YOUR_GITHUB_API_ACCESS_TOKEN 1.19" 3 | 4 | import sys 5 | from collections import Counter 6 | from github import Github 7 | 8 | TOKEN=sys.argv[1] 9 | MILESTONE=sys.argv[2] 10 | g = Github(login_or_token=TOKEN) 11 | 12 | # Then play with your Github objects: 13 | org = g.get_organization("antlr") 14 | repo = org.get_repo("intellij-plugin-v4") 15 | milestone = [x for x in repo.get_milestones() if x.title==MILESTONE] 16 | milestone = milestone[0] 17 | 18 | issues = repo.get_issues(state="closed", milestone=milestone, sort="created", direction="desc") 19 | 20 | # dump bugs fixed 21 | print() 22 | print("## Issues fixed") 23 | for x in issues: 24 | labels = [l.name for l in x.labels] 25 | if x.pull_request is None and not ("type:improvement" in labels or "type:feature" in labels): 26 | print("* [%s](%s) (%s)" % (x.title, x.html_url, ", ".join([l.name for l in x.labels]))) 27 | 28 | # dump improvements closed for this release (issues or pulls) 29 | print() 30 | print("## Improvements, features") 31 | for x in issues: 32 | labels = [l.name for l in x.labels] 33 | if ("type:enhancement" in labels or "type:feature" in labels): 34 | print("* [%s](%s) (%s)" % (x.title, x.html_url, ", ".join(labels))) 35 | 36 | # dump PRs closed for this release 37 | print() 38 | print("## Pull requests") 39 | for x in issues: 40 | labels = [l.name for l in x.labels] 41 | if x.pull_request is not None: 42 | print("* [%s](%s) (%s)" % (x.title, x.html_url, ", ".join(labels))) 43 | 44 | # dump contributors 45 | print() 46 | print("## Contributors") 47 | user_counts = Counter([x.user.login for x in issues]) 48 | users = {x.user.login:x.user for x in issues} 49 | for login,count in user_counts.most_common(10000): 50 | name = users[login].name 51 | logins = f" ({users[login].login})" 52 | if name is None: 53 | name = users[login].login 54 | logins = "" 55 | print(f"* {count:3d} items: [{name}]({users[login].html_url}){logins}") 56 | -------------------------------------------------------------------------------- /src/main/java/org/antlr/intellij/adaptor/parser/PsiElementFactory.java: -------------------------------------------------------------------------------- 1 | package org.antlr.intellij.adaptor.parser; 2 | 3 | import com.intellij.lang.ASTNode; 4 | import com.intellij.psi.PsiElement; 5 | 6 | /** 7 | * This interface supports constructing a {@link PsiElement} from an {@link ASTNode}. 8 | */ 9 | public interface PsiElementFactory { 10 | PsiElement createElement(ASTNode node); 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/org/antlr/intellij/plugin/ANTLRv4ASTFactory.java: -------------------------------------------------------------------------------- 1 | package org.antlr.intellij.plugin; 2 | 3 | import com.intellij.extapi.psi.ASTWrapperPsiElement; 4 | import com.intellij.lang.ASTFactory; 5 | import com.intellij.lang.ASTNode; 6 | import com.intellij.psi.PsiElement; 7 | import com.intellij.psi.impl.source.tree.CompositeElement; 8 | import com.intellij.psi.impl.source.tree.FileElement; 9 | import com.intellij.psi.impl.source.tree.LeafElement; 10 | import com.intellij.psi.impl.source.tree.LeafPsiElement; 11 | import com.intellij.psi.tree.IElementType; 12 | import com.intellij.psi.tree.IFileElementType; 13 | import org.antlr.intellij.adaptor.parser.PsiElementFactory; 14 | import org.antlr.intellij.plugin.parser.ANTLRv4Lexer; 15 | import org.antlr.intellij.plugin.parser.ANTLRv4Parser; 16 | import org.antlr.intellij.plugin.psi.*; 17 | 18 | import java.util.HashMap; 19 | import java.util.Map; 20 | 21 | public class ANTLRv4ASTFactory extends ASTFactory { 22 | private static final Map ruleElementTypeToPsiFactory = new HashMap<>(); 23 | static { 24 | // later auto gen with tokens from some spec in grammar? 25 | ruleElementTypeToPsiFactory.put(ANTLRv4TokenTypes.RULE_ELEMENT_TYPES.get(ANTLRv4Parser.RULE_rules), RulesNode.Factory.INSTANCE); 26 | ruleElementTypeToPsiFactory.put(ANTLRv4TokenTypes.RULE_ELEMENT_TYPES.get(ANTLRv4Parser.RULE_parserRuleSpec), ParserRuleSpecNode.Factory.INSTANCE); 27 | ruleElementTypeToPsiFactory.put(ANTLRv4TokenTypes.RULE_ELEMENT_TYPES.get(ANTLRv4Parser.RULE_lexerRule), LexerRuleSpecNode.Factory.INSTANCE); 28 | ruleElementTypeToPsiFactory.put(ANTLRv4TokenTypes.RULE_ELEMENT_TYPES.get(ANTLRv4Parser.RULE_grammarSpec), GrammarSpecNode.Factory.INSTANCE); 29 | ruleElementTypeToPsiFactory.put(ANTLRv4TokenTypes.RULE_ELEMENT_TYPES.get(ANTLRv4Parser.RULE_modeSpec), ModeSpecNode.Factory.INSTANCE); 30 | ruleElementTypeToPsiFactory.put(ANTLRv4TokenTypes.RULE_ELEMENT_TYPES.get(ANTLRv4Parser.RULE_action), AtAction.Factory.INSTANCE); 31 | ruleElementTypeToPsiFactory.put(ANTLRv4TokenTypes.RULE_ELEMENT_TYPES.get(ANTLRv4Parser.RULE_identifier), TokenSpecNode.Factory.INSTANCE); 32 | } 33 | 34 | /** Create a FileElement for root or a parse tree CompositeElement (not 35 | * PSI) for the token. This impl is more or less the default. 36 | */ 37 | @Override 38 | public CompositeElement createComposite(IElementType type) { 39 | if (type instanceof IFileElementType) { 40 | return new FileElement(type, null); 41 | } 42 | return new CompositeElement(type); 43 | } 44 | 45 | /** Create PSI nodes out of tokens so even parse tree sees them as such. 46 | * Does not see whitespace tokens. 47 | */ 48 | @Override 49 | public LeafElement createLeaf(IElementType type, CharSequence text) { 50 | LeafElement t; 51 | if ( type == ANTLRv4TokenTypes.TOKEN_ELEMENT_TYPES.get(ANTLRv4Lexer.RULE_REF) ) { 52 | t = new ParserRuleRefNode(type, text); 53 | } 54 | else if ( type == ANTLRv4TokenTypes.TOKEN_ELEMENT_TYPES.get(ANTLRv4Lexer.TOKEN_REF) ) { 55 | t = new LexerRuleRefNode(type, text); 56 | } 57 | else if ( type == ANTLRv4TokenTypes.TOKEN_ELEMENT_TYPES.get(ANTLRv4Lexer.STRING_LITERAL) ) { 58 | t = new StringLiteralElement(type, text); 59 | } 60 | else { 61 | t = new LeafPsiElement(type, text); 62 | } 63 | return t; 64 | } 65 | 66 | public static PsiElement createInternalParseTreeNode(ASTNode node) { 67 | PsiElement t; 68 | IElementType tokenType = node.getElementType(); 69 | PsiElementFactory factory = ruleElementTypeToPsiFactory.get(tokenType); 70 | if (factory != null) { 71 | t = factory.createElement(node); 72 | } 73 | else { 74 | t = new ASTWrapperPsiElement(node); 75 | } 76 | 77 | return t; 78 | } 79 | 80 | } 81 | -------------------------------------------------------------------------------- /src/main/java/org/antlr/intellij/plugin/ANTLRv4Commenter.java: -------------------------------------------------------------------------------- 1 | package org.antlr.intellij.plugin; 2 | 3 | import com.intellij.lang.CodeDocumentationAwareCommenter; 4 | import com.intellij.psi.PsiComment; 5 | import com.intellij.psi.tree.IElementType; 6 | import org.antlr.intellij.plugin.parser.ANTLRv4Lexer; 7 | import org.jetbrains.annotations.Nullable; 8 | 9 | /** 10 | * Created by jason on 1/7/15. 11 | */ 12 | 13 | //DOC_COMMENT 14 | // : '/**' .*? ('*/' | EOF) 15 | // ; 16 | //BLOCK_COMMENT 17 | // : '/*' .*? ('*/' | EOF) -> channel(HIDDEN) 18 | // ; 19 | // 20 | //LINE_COMMENT 21 | // : '//' ~[\r\n]* -> channel(HIDDEN) 22 | // ; 23 | 24 | public class ANTLRv4Commenter implements CodeDocumentationAwareCommenter { 25 | @Nullable 26 | @Override 27 | public String getLineCommentPrefix() { 28 | return "//"; 29 | } 30 | 31 | @Nullable 32 | @Override 33 | public String getBlockCommentPrefix() { 34 | return "/*"; 35 | } 36 | 37 | @Nullable 38 | @Override 39 | public String getBlockCommentSuffix() { 40 | return "*/"; 41 | } 42 | 43 | @Nullable 44 | @Override 45 | public String getCommentedBlockCommentPrefix() { 46 | return null; 47 | } 48 | 49 | @Nullable 50 | @Override 51 | public String getCommentedBlockCommentSuffix() { 52 | return null; 53 | } 54 | 55 | @Nullable 56 | @Override 57 | public IElementType getLineCommentTokenType() { 58 | return ANTLRv4TokenTypes.TOKEN_ELEMENT_TYPES.get(ANTLRv4Lexer.LINE_COMMENT); 59 | } 60 | 61 | @Nullable 62 | @Override 63 | public IElementType getBlockCommentTokenType() { 64 | return ANTLRv4TokenTypes.TOKEN_ELEMENT_TYPES.get(ANTLRv4Lexer.BLOCK_COMMENT); 65 | 66 | } 67 | 68 | @Nullable 69 | @Override 70 | public IElementType getDocumentationCommentTokenType() { 71 | return ANTLRv4TokenTypes.TOKEN_ELEMENT_TYPES.get(ANTLRv4Lexer.DOC_COMMENT); 72 | } 73 | 74 | @Nullable 75 | @Override 76 | public String getDocumentationCommentPrefix() { 77 | return "/**"; 78 | } 79 | 80 | @Nullable 81 | @Override 82 | public String getDocumentationCommentLinePrefix() { 83 | //TODO: this isnt specified in the grammar. remove? 84 | return "*"; 85 | } 86 | 87 | @Nullable 88 | @Override 89 | public String getDocumentationCommentSuffix() { 90 | return "*/"; 91 | } 92 | 93 | @Override 94 | public boolean isDocumentationComment(PsiComment element) { 95 | return element != null && element.getTokenType() == getDocumentationCommentTokenType(); 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /src/main/java/org/antlr/intellij/plugin/ANTLRv4FileRoot.java: -------------------------------------------------------------------------------- 1 | package org.antlr.intellij.plugin; 2 | 3 | import com.intellij.extapi.psi.PsiFileBase; 4 | import com.intellij.openapi.fileTypes.FileType; 5 | import com.intellij.psi.FileViewProvider; 6 | import org.jetbrains.annotations.NotNull; 7 | 8 | public class ANTLRv4FileRoot extends PsiFileBase { 9 | public ANTLRv4FileRoot(@NotNull FileViewProvider viewProvider) { 10 | super(viewProvider, ANTLRv4Language.INSTANCE); 11 | } 12 | 13 | @NotNull 14 | @Override 15 | public FileType getFileType() { 16 | return ANTLRv4FileType.INSTANCE; 17 | } 18 | 19 | @Override 20 | public String toString() { 21 | return "ANTLR v4 grammar file"; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/org/antlr/intellij/plugin/ANTLRv4FileType.java: -------------------------------------------------------------------------------- 1 | package org.antlr.intellij.plugin; 2 | 3 | import com.intellij.openapi.fileTypes.LanguageFileType; 4 | import org.jetbrains.annotations.NotNull; 5 | import org.jetbrains.annotations.Nullable; 6 | 7 | import javax.swing.*; 8 | 9 | public class ANTLRv4FileType extends LanguageFileType { 10 | public static final ANTLRv4FileType INSTANCE = new ANTLRv4FileType(); 11 | 12 | private ANTLRv4FileType() { 13 | super(ANTLRv4Language.INSTANCE); 14 | } 15 | 16 | @NotNull 17 | @Override 18 | public String getName() { 19 | return "ANTLR v4 grammar file"; 20 | } 21 | 22 | @NotNull 23 | @Override 24 | public String getDescription() { 25 | return "ANTLR v4 grammar file"; 26 | } 27 | 28 | @NotNull 29 | @Override 30 | public String getDefaultExtension() { 31 | return "g4"; 32 | } 33 | 34 | @Nullable 35 | @Override 36 | public Icon getIcon() { 37 | return Icons.FILE; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/org/antlr/intellij/plugin/ANTLRv4FileTypeFactory.java: -------------------------------------------------------------------------------- 1 | package org.antlr.intellij.plugin; 2 | 3 | import com.intellij.openapi.fileTypes.FileTypeConsumer; 4 | import com.intellij.openapi.fileTypes.FileTypeFactory; 5 | import org.jetbrains.annotations.NotNull; 6 | 7 | public class ANTLRv4FileTypeFactory extends FileTypeFactory{ 8 | @Override 9 | public void createFileTypes(@NotNull FileTypeConsumer fileTypeConsumer) { 10 | fileTypeConsumer.consume(ANTLRv4FileType.INSTANCE, "g4"); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/org/antlr/intellij/plugin/ANTLRv4FindUsagesProvider.java: -------------------------------------------------------------------------------- 1 | package org.antlr.intellij.plugin; 2 | 3 | import com.intellij.lang.cacheBuilder.WordsScanner; 4 | import com.intellij.lang.findUsages.FindUsagesProvider; 5 | import com.intellij.psi.PsiElement; 6 | import com.intellij.psi.util.PsiTreeUtil; 7 | import org.antlr.intellij.plugin.psi.*; 8 | import org.jetbrains.annotations.NotNull; 9 | import org.jetbrains.annotations.Nullable; 10 | 11 | public class ANTLRv4FindUsagesProvider implements FindUsagesProvider { 12 | @Override 13 | public boolean canFindUsagesFor(@NotNull PsiElement psiElement) { 14 | return psiElement instanceof RuleSpecNode; 15 | } 16 | 17 | @Nullable 18 | @Override 19 | public WordsScanner getWordsScanner() { 20 | return null; // seems ok as JavaFindUsagesProvider does same thing 21 | } 22 | 23 | @Nullable 24 | @Override 25 | public String getHelpId(@NotNull PsiElement element) { 26 | return null; 27 | } 28 | 29 | @NotNull 30 | @Override 31 | public String getType(@NotNull PsiElement element) { 32 | if (element instanceof ParserRuleSpecNode) { 33 | return "parser rule"; 34 | } 35 | if (element instanceof LexerRuleSpecNode) { 36 | return "lexer rule"; 37 | } 38 | if (element instanceof ModeSpecNode) { 39 | return "mode"; 40 | } 41 | if (element instanceof TokenSpecNode) { 42 | return "token"; 43 | } 44 | if (element instanceof ChannelSpecNode) { 45 | return "channel"; 46 | } 47 | return "n/a"; 48 | } 49 | 50 | @NotNull 51 | @Override 52 | public String getDescriptiveName(@NotNull PsiElement element) { 53 | PsiElement rule = PsiTreeUtil.findChildOfAnyType(element, LexerRuleRefNode.class, ParserRuleRefNode.class); 54 | if ( rule!=null ) return rule.getText(); 55 | return "n/a"; 56 | } 57 | 58 | @NotNull 59 | @Override 60 | public String getNodeText(@NotNull PsiElement element, boolean useFullName) { 61 | return getDescriptiveName(element); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/main/java/org/antlr/intellij/plugin/ANTLRv4IconProvider.java: -------------------------------------------------------------------------------- 1 | package org.antlr.intellij.plugin; 2 | 3 | import com.intellij.ide.IconProvider; 4 | import com.intellij.psi.PsiElement; 5 | import org.antlr.intellij.plugin.psi.LexerRuleRefNode; 6 | import org.antlr.intellij.plugin.psi.ModeSpecNode; 7 | import org.antlr.intellij.plugin.psi.ParserRuleRefNode; 8 | import org.jetbrains.annotations.NotNull; 9 | import org.jetbrains.annotations.Nullable; 10 | 11 | import javax.swing.*; 12 | 13 | public class ANTLRv4IconProvider extends IconProvider { 14 | 15 | @Nullable 16 | @Override 17 | public Icon getIcon(@NotNull PsiElement element, int flags) { 18 | if ( element instanceof LexerRuleRefNode ) { 19 | return Icons.LEXER_RULE; 20 | } 21 | else if ( element instanceof ParserRuleRefNode ) { 22 | return Icons.PARSER_RULE; 23 | } 24 | else if ( element instanceof ANTLRv4FileRoot ) { 25 | return Icons.FILE; 26 | } 27 | else if ( element instanceof ModeSpecNode ) { 28 | return Icons.MODE; 29 | } 30 | return null; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/org/antlr/intellij/plugin/ANTLRv4Language.java: -------------------------------------------------------------------------------- 1 | package org.antlr.intellij.plugin; 2 | 3 | import com.intellij.lang.Language; 4 | 5 | public class ANTLRv4Language extends Language { 6 | public static final ANTLRv4Language INSTANCE = new ANTLRv4Language(); 7 | 8 | private ANTLRv4Language() { 9 | super("ANTLRv4"); 10 | } 11 | 12 | 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/org/antlr/intellij/plugin/ANTLRv4ParserDefinition.java: -------------------------------------------------------------------------------- 1 | package org.antlr.intellij.plugin; 2 | 3 | import com.intellij.lang.ASTNode; 4 | import com.intellij.lang.ParserDefinition; 5 | import com.intellij.lang.PsiParser; 6 | import com.intellij.lexer.Lexer; 7 | import com.intellij.openapi.project.Project; 8 | import com.intellij.psi.FileViewProvider; 9 | import com.intellij.psi.PsiElement; 10 | import com.intellij.psi.PsiFile; 11 | import com.intellij.psi.tree.IFileElementType; 12 | import com.intellij.psi.tree.TokenSet; 13 | import org.antlr.intellij.plugin.adaptors.ANTLRv4GrammarParser; 14 | import org.antlr.intellij.plugin.adaptors.ANTLRv4LexerAdaptor; 15 | import org.antlr.intellij.plugin.parser.ANTLRv4Lexer; 16 | import org.jetbrains.annotations.NotNull; 17 | 18 | /** The general interface between IDEA and ANTLR. */ 19 | public class ANTLRv4ParserDefinition implements ParserDefinition { 20 | public static final IFileElementType FILE = 21 | new IFileElementType(ANTLRv4Language.INSTANCE); 22 | 23 | @NotNull 24 | @Override 25 | public Lexer createLexer(Project project) { 26 | ANTLRv4Lexer lexer = new ANTLRv4Lexer(null); 27 | return new ANTLRv4LexerAdaptor(lexer); 28 | } 29 | 30 | @NotNull 31 | public PsiParser createParser(final Project project) { 32 | return new ANTLRv4GrammarParser(); 33 | } 34 | 35 | @NotNull 36 | public TokenSet getWhitespaceTokens() { 37 | return ANTLRv4TokenTypes.WHITESPACES; 38 | } 39 | 40 | @NotNull 41 | public TokenSet getCommentTokens() { 42 | return ANTLRv4TokenTypes.COMMENTS; 43 | } 44 | 45 | @NotNull 46 | public TokenSet getStringLiteralElements() { 47 | return TokenSet.EMPTY; 48 | } 49 | 50 | @Override 51 | public IFileElementType getFileNodeType() { 52 | return FILE; 53 | } 54 | 55 | @Override 56 | public PsiFile createFile(FileViewProvider viewProvider) { 57 | return new ANTLRv4FileRoot(viewProvider); 58 | } 59 | 60 | public SpaceRequirements spaceExistanceTypeBetweenTokens(ASTNode left, ASTNode right) { 61 | return SpaceRequirements.MAY; 62 | } 63 | 64 | /** Convert from internal parse node (AST they call it) to final PSI node. This 65 | * converts only internal rule nodes apparently, not leaf nodes. Leaves 66 | * are just tokens I guess. 67 | */ 68 | @NotNull 69 | public PsiElement createElement(ASTNode node) { 70 | return ANTLRv4ASTFactory.createInternalParseTreeNode(node); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/main/java/org/antlr/intellij/plugin/ANTLRv4SyntaxHighlighter.java: -------------------------------------------------------------------------------- 1 | package org.antlr.intellij.plugin; 2 | 3 | import com.intellij.lexer.Lexer; 4 | import com.intellij.openapi.editor.DefaultLanguageHighlighterColors; 5 | import com.intellij.openapi.editor.HighlighterColors; 6 | import com.intellij.openapi.editor.colors.TextAttributesKey; 7 | import com.intellij.openapi.fileTypes.SyntaxHighlighterBase; 8 | import com.intellij.psi.tree.IElementType; 9 | import org.antlr.intellij.plugin.adaptors.ANTLRv4LexerAdaptor; 10 | import org.antlr.intellij.plugin.parser.ANTLRv4Lexer; 11 | import org.jetbrains.annotations.NotNull; 12 | 13 | import static com.intellij.openapi.editor.colors.TextAttributesKey.createTextAttributesKey; 14 | 15 | public class ANTLRv4SyntaxHighlighter extends SyntaxHighlighterBase { 16 | public static final TextAttributesKey KEYWORD = 17 | createTextAttributesKey("ANTLRv4_KEYWORD", DefaultLanguageHighlighterColors.KEYWORD); 18 | public static final TextAttributesKey RULENAME = 19 | createTextAttributesKey("ANTLRv4_RULENAME", DefaultLanguageHighlighterColors.PARAMETER); 20 | public static final TextAttributesKey TOKENNAME = 21 | createTextAttributesKey("ANTLRv4_TOKENNAME", DefaultLanguageHighlighterColors.INSTANCE_FIELD); 22 | public static final TextAttributesKey STRING = 23 | createTextAttributesKey("ANTLRv4_STRING", DefaultLanguageHighlighterColors.STRING); 24 | public static final TextAttributesKey LINE_COMMENT = 25 | createTextAttributesKey("ANTLRv4_LINE_COMMENT", DefaultLanguageHighlighterColors.LINE_COMMENT); 26 | public static final TextAttributesKey DOC_COMMENT = 27 | createTextAttributesKey("ANTLRv4_DOC_COMMENT", DefaultLanguageHighlighterColors.DOC_COMMENT); 28 | public static final TextAttributesKey BLOCK_COMMENT = 29 | createTextAttributesKey("ANTLRv4_BLOCK_COMMENT", DefaultLanguageHighlighterColors.BLOCK_COMMENT); 30 | 31 | private static final TextAttributesKey[] BAD_CHAR_KEYS = pack(HighlighterColors.BAD_CHARACTER); 32 | private static final TextAttributesKey[] STRING_KEYS = pack(STRING); 33 | private static final TextAttributesKey[] COMMENT_KEYS = new TextAttributesKey[] {LINE_COMMENT, DOC_COMMENT, BLOCK_COMMENT}; 34 | private static final TextAttributesKey[] EMPTY_KEYS = new TextAttributesKey[0]; 35 | 36 | @NotNull 37 | @Override 38 | public Lexer getHighlightingLexer() { 39 | ANTLRv4Lexer lexer = new ANTLRv4Lexer(null); 40 | return new ANTLRv4LexerAdaptor(lexer); 41 | } 42 | 43 | @NotNull 44 | @Override 45 | public TextAttributesKey[] getTokenHighlights(IElementType tokenType) { 46 | if ( ANTLRv4TokenTypes.KEYWORDS.contains(tokenType) ){ 47 | return pack(KEYWORD); 48 | } 49 | 50 | if ( tokenType == ANTLRv4TokenTypes.TOKEN_ELEMENT_TYPES.get(ANTLRv4Lexer.TOKEN_REF) ) { 51 | return pack(TOKENNAME); 52 | } 53 | else if ( tokenType == ANTLRv4TokenTypes.TOKEN_ELEMENT_TYPES.get(ANTLRv4Lexer.RULE_REF) ) { 54 | return pack(RULENAME); 55 | } 56 | else if (tokenType == ANTLRv4TokenTypes.TOKEN_ELEMENT_TYPES.get(ANTLRv4Lexer.STRING_LITERAL) 57 | || tokenType == ANTLRv4TokenTypes.TOKEN_ELEMENT_TYPES.get(ANTLRv4Lexer.UNTERMINATED_STRING_LITERAL)) { 58 | return STRING_KEYS; 59 | } 60 | else if (tokenType == ANTLRv4TokenTypes.TOKEN_ELEMENT_TYPES.get(ANTLRv4Lexer.BLOCK_COMMENT)) { 61 | return COMMENT_KEYS; 62 | } 63 | else if (tokenType == ANTLRv4TokenTypes.TOKEN_ELEMENT_TYPES.get(ANTLRv4Lexer.DOC_COMMENT)) { 64 | return COMMENT_KEYS; 65 | } 66 | else if (tokenType == ANTLRv4TokenTypes.TOKEN_ELEMENT_TYPES.get(ANTLRv4Lexer.LINE_COMMENT)) { 67 | return COMMENT_KEYS; 68 | } 69 | else if (tokenType == ANTLRv4TokenTypes.BAD_TOKEN_TYPE) { 70 | return BAD_CHAR_KEYS; 71 | } 72 | else { 73 | return EMPTY_KEYS; 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/main/java/org/antlr/intellij/plugin/ANTLRv4SyntaxHighlighterFactory.java: -------------------------------------------------------------------------------- 1 | package org.antlr.intellij.plugin; 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 | import org.jetbrains.annotations.NotNull; 8 | 9 | public class ANTLRv4SyntaxHighlighterFactory extends SyntaxHighlighterFactory { 10 | @NotNull 11 | @Override 12 | public SyntaxHighlighter getSyntaxHighlighter(Project project, VirtualFile virtualFile) { 13 | return new ANTLRv4SyntaxHighlighter(); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/org/antlr/intellij/plugin/ANTLRv4TokenTypes.java: -------------------------------------------------------------------------------- 1 | package org.antlr.intellij.plugin; 2 | 3 | import com.intellij.psi.tree.IElementType; 4 | import com.intellij.psi.tree.TokenSet; 5 | import org.antlr.intellij.adaptor.lexer.PSIElementTypeFactory; 6 | import org.antlr.intellij.adaptor.lexer.RuleIElementType; 7 | import org.antlr.intellij.adaptor.lexer.TokenIElementType; 8 | import org.antlr.intellij.plugin.adaptors.ANTLRv4LexerAdaptor; 9 | import org.antlr.intellij.plugin.parser.ANTLRv4Lexer; 10 | import org.antlr.intellij.plugin.parser.ANTLRv4Parser; 11 | import org.intellij.lang.annotations.MagicConstant; 12 | 13 | import java.util.List; 14 | 15 | public class ANTLRv4TokenTypes { 16 | public static IElementType BAD_TOKEN_TYPE = new IElementType("BAD_TOKEN", ANTLRv4Language.INSTANCE); 17 | 18 | static { 19 | ANTLRv4LexerAdaptor.initializeElementTypeFactory(); 20 | } 21 | 22 | public static final List TOKEN_ELEMENT_TYPES = 23 | PSIElementTypeFactory.getTokenIElementTypes(ANTLRv4Language.INSTANCE); 24 | public static final List RULE_ELEMENT_TYPES = 25 | PSIElementTypeFactory.getRuleIElementTypes(ANTLRv4Language.INSTANCE); 26 | 27 | public static final TokenSet COMMENTS = 28 | PSIElementTypeFactory.createTokenSet( 29 | ANTLRv4Language.INSTANCE, 30 | ANTLRv4Lexer.DOC_COMMENT, 31 | ANTLRv4Lexer.BLOCK_COMMENT, 32 | ANTLRv4Lexer.LINE_COMMENT); 33 | 34 | public static final TokenSet WHITESPACES = 35 | PSIElementTypeFactory.createTokenSet( 36 | ANTLRv4Language.INSTANCE, 37 | ANTLRv4Lexer.WS); 38 | 39 | public static final TokenSet KEYWORDS = 40 | PSIElementTypeFactory.createTokenSet( 41 | ANTLRv4Language.INSTANCE, 42 | ANTLRv4Lexer.LEXER,ANTLRv4Lexer.PROTECTED,ANTLRv4Lexer.IMPORT,ANTLRv4Lexer.CATCH, 43 | ANTLRv4Lexer.PRIVATE,ANTLRv4Lexer.FRAGMENT,ANTLRv4Lexer.PUBLIC,ANTLRv4Lexer.MODE, 44 | ANTLRv4Lexer.FINALLY,ANTLRv4Lexer.RETURNS,ANTLRv4Lexer.THROWS,ANTLRv4Lexer.GRAMMAR, 45 | ANTLRv4Lexer.LOCALS,ANTLRv4Lexer.PARSER); 46 | 47 | public static RuleIElementType getRuleElementType(@MagicConstant(valuesFromClass = ANTLRv4Parser.class)int ruleIndex){ 48 | return RULE_ELEMENT_TYPES.get(ruleIndex); 49 | } 50 | public static TokenIElementType getTokenElementType(@MagicConstant(valuesFromClass = ANTLRv4Lexer.class)int ruleIndex){ 51 | return TOKEN_ELEMENT_TYPES.get(ruleIndex); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/org/antlr/intellij/plugin/Icons.java: -------------------------------------------------------------------------------- 1 | package org.antlr.intellij.plugin; 2 | 3 | import com.intellij.openapi.application.ApplicationInfo; 4 | import com.intellij.openapi.util.IconLoader; 5 | 6 | import javax.swing.*; 7 | 8 | public class Icons { 9 | public static final Icon FILE = IconLoader.getIcon("/icons/org/antlr/intellij/plugin/antlr.png"); 10 | public static final Icon LEXER_RULE = IconLoader.getIcon("/icons/org/antlr/intellij/plugin/lexer-rule.png"); 11 | public static final Icon PARSER_RULE = IconLoader.getIcon("/icons/org/antlr/intellij/plugin/parser-rule.png"); 12 | public static final Icon MODE = IconLoader.getIcon("/icons/org/antlr/intellij/plugin/mode.png"); 13 | 14 | public static Icon getToolWindow() { 15 | // IntelliJ 2018.2+ has monochrome icons for tool windows so let's use one too 16 | if (ApplicationInfo.getInstance().getBuild().getBaselineVersion() >= 182) { 17 | return IconLoader.getIcon("/icons/org/antlr/intellij/plugin/toolWindowAntlr.svg"); 18 | } 19 | 20 | return IconLoader.getIcon("/icons/org/antlr/intellij/plugin/antlr.png"); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/org/antlr/intellij/plugin/PluginIgnoreMissingTokensFileErrorManager.java: -------------------------------------------------------------------------------- 1 | package org.antlr.intellij.plugin; 2 | 3 | import org.antlr.v4.Tool; 4 | import org.antlr.v4.tool.ANTLRMessage; 5 | import org.antlr.v4.tool.ErrorManager; 6 | import org.antlr.v4.tool.ErrorType; 7 | 8 | public class PluginIgnoreMissingTokensFileErrorManager extends ErrorManager { 9 | public PluginIgnoreMissingTokensFileErrorManager(Tool tool) { 10 | super(tool); 11 | } 12 | 13 | @Override 14 | public void emit(ErrorType etype, ANTLRMessage msg) { 15 | if ( etype==ErrorType.CANNOT_FIND_TOKENS_FILE_REFD_IN_GRAMMAR || 16 | etype==ErrorType.CANNOT_FIND_TOKENS_FILE_GIVEN_ON_CMDLINE ) 17 | { 18 | return; // ignore these 19 | } 20 | super.emit(etype, msg); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/org/antlr/intellij/plugin/Utils.java: -------------------------------------------------------------------------------- 1 | package org.antlr.intellij.plugin; 2 | 3 | import com.intellij.util.containers.Predicate; 4 | 5 | import java.util.ArrayList; 6 | import java.util.Collection; 7 | import java.util.List; 8 | 9 | public class Utils { 10 | public static List filter(Collection data, Predicate pred) { 11 | if ( data==null ) return null; 12 | List filtered = new ArrayList<>(); 13 | for (T x : data) { 14 | if ( pred.apply(x) ) filtered.add(x); 15 | } 16 | return filtered; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/org/antlr/intellij/plugin/actions/AnnotationIntentActionsFactory.java: -------------------------------------------------------------------------------- 1 | package org.antlr.intellij.plugin.actions; 2 | 3 | import com.intellij.codeInsight.intention.IntentionAction; 4 | import com.intellij.openapi.util.TextRange; 5 | import com.intellij.psi.PsiFile; 6 | import org.antlr.intellij.plugin.validation.AddTokenDefinitionFix; 7 | import org.antlr.intellij.plugin.validation.CreateRuleFix; 8 | import org.antlr.v4.tool.ErrorType; 9 | import org.jetbrains.annotations.NotNull; 10 | 11 | import java.util.Optional; 12 | 13 | public class AnnotationIntentActionsFactory { 14 | @NotNull 15 | public static Optional getFix(TextRange textRange, ErrorType errorType, PsiFile file) { 16 | if (errorType == ErrorType.IMPLICIT_TOKEN_DEFINITION) { 17 | return Optional.of(new AddTokenDefinitionFix(textRange)); 18 | } 19 | else if ( errorType==ErrorType.UNDEFINED_RULE_REF ) { 20 | return Optional.of(new CreateRuleFix(textRange, file)); 21 | } 22 | return Optional.empty(); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/org/antlr/intellij/plugin/actions/ChooseExtractedRuleName.java: -------------------------------------------------------------------------------- 1 | package org.antlr.intellij.plugin.actions; 2 | 3 | import com.intellij.openapi.project.Project; 4 | import com.intellij.openapi.ui.DialogWrapper; 5 | import com.intellij.ui.components.JBTextField; 6 | import org.jetbrains.annotations.Nullable; 7 | 8 | import javax.swing.*; 9 | import java.awt.*; 10 | 11 | public class ChooseExtractedRuleName extends DialogWrapper { 12 | JBTextField nameField; 13 | public String ruleName; 14 | 15 | protected ChooseExtractedRuleName(@Nullable Project project) { 16 | super(project, true); 17 | init(); 18 | } 19 | 20 | @Override 21 | protected void doOKAction() { 22 | super.doOKAction(); 23 | ruleName = nameField.getText(); 24 | } 25 | 26 | @Override 27 | protected JComponent createCenterPanel() { 28 | nameField = new JBTextField("newRule"); 29 | double h = nameField.getSize().getHeight(); 30 | nameField.setPreferredSize(new Dimension(250,(int)h)); 31 | setTitle("Name the extracted rule"); 32 | nameField.selectAll(); 33 | return nameField; 34 | } 35 | 36 | @Nullable 37 | @Override 38 | public JComponent getPreferredFocusedComponent() { 39 | return nameField; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/org/antlr/intellij/plugin/actions/ConfigureANTLRAction.java: -------------------------------------------------------------------------------- 1 | package org.antlr.intellij.plugin.actions; 2 | 3 | import com.intellij.openapi.actionSystem.ActionUpdateThread; 4 | import com.intellij.openapi.actionSystem.AnAction; 5 | import com.intellij.openapi.actionSystem.AnActionEvent; 6 | import com.intellij.openapi.diagnostic.Logger; 7 | import com.intellij.openapi.project.DumbAware; 8 | import com.intellij.openapi.ui.DialogWrapper; 9 | import com.intellij.openapi.vfs.VirtualFile; 10 | import org.antlr.intellij.plugin.configdialogs.ConfigANTLRPerGrammar; 11 | import org.antlr.v4.Tool; 12 | import org.jetbrains.annotations.NotNull; 13 | 14 | public class ConfigureANTLRAction extends AnAction implements DumbAware { 15 | public static final Logger LOG = Logger.getInstance("ConfigureANTLRAction"); 16 | 17 | @Override 18 | public void update(AnActionEvent e) { 19 | MyActionUtils.selectedFileIsGrammar(e); 20 | } 21 | 22 | @Override 23 | public @NotNull ActionUpdateThread getActionUpdateThread() { 24 | return ActionUpdateThread.BGT; 25 | } 26 | 27 | @Override 28 | public void actionPerformed(AnActionEvent e) { 29 | if ( e.getProject()==null ) { 30 | LOG.error("actionPerformed no project for "+e); 31 | return; // whoa! 32 | } 33 | VirtualFile grammarFile = MyActionUtils.getGrammarFileFromEvent(e); 34 | if ( grammarFile==null ) return; 35 | LOG.info("actionPerformed "+grammarFile); 36 | 37 | ConfigANTLRPerGrammar configDialog = ConfigANTLRPerGrammar.getDialogForm(e.getProject(), grammarFile.getPath()); 38 | configDialog.getPeer().setTitle("Configure ANTLR Tool "+ Tool.VERSION+" for "+ grammarFile.getName()); 39 | 40 | configDialog.show(); 41 | 42 | if ( configDialog.getExitCode()==DialogWrapper.OK_EXIT_CODE ) { 43 | configDialog.saveValues(e.getProject(), grammarFile.getPath()); 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/org/antlr/intellij/plugin/adaptors/ANTLRv4GrammarParser.java: -------------------------------------------------------------------------------- 1 | package org.antlr.intellij.plugin.adaptors; 2 | 3 | import com.intellij.psi.tree.IElementType; 4 | import com.intellij.psi.tree.IFileElementType; 5 | import org.antlr.intellij.adaptor.parser.ANTLRParserAdaptor; 6 | import org.antlr.intellij.plugin.ANTLRv4Language; 7 | import org.antlr.intellij.plugin.ANTLRv4TokenTypes; 8 | import org.antlr.intellij.plugin.parser.ANTLRv4Lexer; 9 | import org.antlr.intellij.plugin.parser.ANTLRv4Parser; 10 | import org.antlr.v4.runtime.Parser; 11 | import org.antlr.v4.runtime.Token; 12 | import org.antlr.v4.runtime.tree.ParseTree; 13 | 14 | /** A specific kind of parser that knows how to parse ANTLR v4 grammar meta-language */ 15 | public class ANTLRv4GrammarParser extends ANTLRParserAdaptor { 16 | public ANTLRv4GrammarParser() { 17 | super(ANTLRv4Language.INSTANCE, new ANTLRv4Parser(null)); 18 | } 19 | 20 | @Override 21 | protected ParseTree parse(Parser parser, IElementType root) { 22 | int startRule; 23 | if (root instanceof IFileElementType) { 24 | startRule = ANTLRv4Parser.RULE_grammarSpec; 25 | } 26 | else if (root == ANTLRv4TokenTypes.TOKEN_ELEMENT_TYPES.get(ANTLRv4Lexer.TOKEN_REF) 27 | || root == ANTLRv4TokenTypes.TOKEN_ELEMENT_TYPES.get(ANTLRv4Lexer.RULE_REF)) { 28 | startRule = ANTLRv4Parser.RULE_atom; 29 | } 30 | else { 31 | startRule = Token.INVALID_TYPE; 32 | } 33 | 34 | switch (startRule) { 35 | case ANTLRv4Parser.RULE_grammarSpec: 36 | return ((ANTLRv4Parser) parser).grammarSpec(); 37 | 38 | case ANTLRv4Parser.RULE_atom: 39 | return ((ANTLRv4Parser) parser).atom(); 40 | 41 | default: 42 | throw new UnsupportedOperationException(String.format("cannot start parsing using root element %s", root)); 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/org/antlr/intellij/plugin/adaptors/ANTLRv4LexerAdaptor.java: -------------------------------------------------------------------------------- 1 | package org.antlr.intellij.plugin.adaptors; 2 | 3 | import org.antlr.intellij.adaptor.lexer.ANTLRLexerAdaptor; 4 | import org.antlr.intellij.adaptor.lexer.PSIElementTypeFactory; 5 | import org.antlr.intellij.plugin.ANTLRv4Language; 6 | import org.antlr.intellij.plugin.parser.ANTLRv4Lexer; 7 | import org.antlr.intellij.plugin.parser.ANTLRv4Parser; 8 | import org.antlr.v4.runtime.Lexer; 9 | 10 | /** Adapt ANTLR needs to intellij */ 11 | public class ANTLRv4LexerAdaptor extends ANTLRLexerAdaptor { 12 | 13 | // In case a lexer was created outside our ParserDefinition 14 | static { 15 | initializeElementTypeFactory(); 16 | } 17 | 18 | public static void initializeElementTypeFactory() { 19 | PSIElementTypeFactory.defineLanguageIElementTypes( 20 | ANTLRv4Language.INSTANCE, 21 | ANTLRv4Lexer.tokenNames, 22 | ANTLRv4Parser.ruleNames 23 | ); 24 | } 25 | 26 | private static final ANTLRv4LexerState INITIAL_STATE = new ANTLRv4LexerState(Lexer.DEFAULT_MODE, null, 0); 27 | 28 | public ANTLRv4LexerAdaptor(ANTLRv4Lexer lexer) { 29 | super(ANTLRv4Language.INSTANCE, lexer); 30 | } 31 | 32 | @Override 33 | protected ANTLRv4LexerState getInitialState() { 34 | return INITIAL_STATE; 35 | } 36 | 37 | @Override 38 | protected ANTLRv4LexerState getLexerState(Lexer lexer) { 39 | if (lexer._modeStack.isEmpty()) { 40 | return new ANTLRv4LexerState(lexer._mode, null, ((ANTLRv4Lexer)lexer).getCurrentRuleType()); 41 | } 42 | 43 | return new ANTLRv4LexerState(lexer._mode, lexer._modeStack, ((ANTLRv4Lexer)lexer).getCurrentRuleType()); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/org/antlr/intellij/plugin/adaptors/ANTLRv4LexerState.java: -------------------------------------------------------------------------------- 1 | package org.antlr.intellij.plugin.adaptors; 2 | 3 | import org.antlr.intellij.adaptor.lexer.ANTLRLexerState; 4 | import org.antlr.intellij.plugin.parser.ANTLRv4Lexer; 5 | import org.antlr.v4.runtime.Lexer; 6 | import org.antlr.v4.runtime.misc.IntegerStack; 7 | import org.antlr.v4.runtime.misc.MurmurHash; 8 | 9 | public class ANTLRv4LexerState extends ANTLRLexerState { 10 | /** Tracks whether we are in a lexer rule, a parser rule or neither; 11 | * managed by the ANTLRv4Lexer grammar. 12 | */ 13 | private final int currentRuleType; 14 | 15 | public ANTLRv4LexerState(int mode, IntegerStack modeStack, int currentRuleType) { 16 | super(mode, modeStack); 17 | this.currentRuleType = currentRuleType; 18 | } 19 | 20 | public int getCurrentRuleType() { 21 | return currentRuleType; 22 | } 23 | 24 | @Override 25 | public void apply(Lexer lexer) { 26 | super.apply(lexer); 27 | if (lexer instanceof ANTLRv4Lexer) { 28 | ((ANTLRv4Lexer)lexer).setCurrentRuleType(getCurrentRuleType()); 29 | } 30 | } 31 | 32 | @Override 33 | protected int hashCodeImpl() { 34 | int hash = MurmurHash.initialize(); 35 | hash = MurmurHash.update(hash, getMode()); 36 | hash = MurmurHash.update(hash, getModeStack()); 37 | hash = MurmurHash.update(hash, getCurrentRuleType()); 38 | return MurmurHash.finish(hash, 3); 39 | } 40 | 41 | @Override 42 | public boolean equals(Object obj) { 43 | if (obj == this) { 44 | return true; 45 | } 46 | 47 | if (!(obj instanceof ANTLRv4LexerState)) { 48 | return false; 49 | } 50 | 51 | if (!super.equals(obj)) { 52 | return false; 53 | } 54 | 55 | ANTLRv4LexerState other = (ANTLRv4LexerState)obj; 56 | return this.getCurrentRuleType() == other.getCurrentRuleType(); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/main/java/org/antlr/intellij/plugin/configdialogs/ANTLRv4ColorsPage.java: -------------------------------------------------------------------------------- 1 | package org.antlr.intellij.plugin.configdialogs; 2 | 3 | import com.intellij.openapi.editor.colors.TextAttributesKey; 4 | import com.intellij.openapi.fileTypes.SyntaxHighlighter; 5 | import com.intellij.openapi.options.colors.AttributesDescriptor; 6 | import com.intellij.openapi.options.colors.ColorDescriptor; 7 | import com.intellij.openapi.options.colors.ColorSettingsPage; 8 | import org.antlr.intellij.plugin.ANTLRv4SyntaxHighlighter; 9 | import org.antlr.intellij.plugin.Icons; 10 | import org.jetbrains.annotations.NotNull; 11 | import org.jetbrains.annotations.Nullable; 12 | 13 | import javax.swing.*; 14 | import java.util.Map; 15 | 16 | public class ANTLRv4ColorsPage implements ColorSettingsPage { 17 | private static final AttributesDescriptor[] ATTRIBUTES = 18 | { 19 | new AttributesDescriptor("Lexer Rule", ANTLRv4SyntaxHighlighter.TOKENNAME), 20 | new AttributesDescriptor("Parser Rule", ANTLRv4SyntaxHighlighter.RULENAME), 21 | }; 22 | 23 | @Nullable 24 | @Override 25 | public Icon getIcon() { 26 | return Icons.FILE; 27 | } 28 | 29 | @NotNull 30 | @Override 31 | public SyntaxHighlighter getHighlighter() { 32 | return new ANTLRv4SyntaxHighlighter(); 33 | } 34 | 35 | @NotNull 36 | @Override 37 | public String getDemoText() { 38 | return 39 | "grammar Foo;\n" + 40 | "\n" + 41 | "compilationUnit : STUFF EOF;\n" + 42 | "\n" + 43 | "STUFF : 'stuff' -> pushMode(OTHER_MODE);\n" + 44 | "WS : [ \\t]+ -> channel(HIDDEN);\n" + 45 | "NEWLINE : [\\r\\n]+ -> type(WS);\n" + 46 | "BAD_CHAR : . -> skip;\n" + 47 | "\n" + 48 | "mode OTHER_MODE;\n" + 49 | "\n" + 50 | "KEYWORD : 'keyword' -> popMode;\n"; 51 | } 52 | 53 | @Nullable 54 | @Override 55 | public Map getAdditionalHighlightingTagToDescriptorMap() { 56 | return null; 57 | } 58 | 59 | @NotNull 60 | @Override 61 | public AttributesDescriptor[] getAttributeDescriptors() { 62 | return ATTRIBUTES; 63 | } 64 | 65 | @NotNull 66 | @Override 67 | public ColorDescriptor[] getColorDescriptors() { 68 | return ColorDescriptor.EMPTY_ARRAY; 69 | } 70 | 71 | @NotNull 72 | @Override 73 | public String getDisplayName() { 74 | return "ANTLR"; 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/main/java/org/antlr/intellij/plugin/configdialogs/ANTLRv4GrammarPropertiesComponent.java: -------------------------------------------------------------------------------- 1 | package org.antlr.intellij.plugin.configdialogs; 2 | 3 | import com.intellij.openapi.components.PersistentStateComponent; 4 | import com.intellij.openapi.components.ServiceManager; 5 | import com.intellij.openapi.components.State; 6 | import com.intellij.openapi.project.Project; 7 | import org.jetbrains.annotations.NotNull; 8 | 9 | /** 10 | * Stores code generation preferences in .idea/misc.xml. 11 | */ 12 | @State(name = "ANTLRGenerationPreferences") 13 | public class ANTLRv4GrammarPropertiesComponent implements PersistentStateComponent { 14 | 15 | private ANTLRv4GrammarPropertiesStore mySettings = new ANTLRv4GrammarPropertiesStore(); 16 | 17 | public static ANTLRv4GrammarPropertiesComponent getInstance(Project project) { 18 | return ServiceManager.getService(project, ANTLRv4GrammarPropertiesComponent.class); 19 | } 20 | 21 | @NotNull 22 | @Override 23 | public ANTLRv4GrammarPropertiesStore getState() { 24 | return mySettings; 25 | } 26 | 27 | @Override 28 | public void loadState(ANTLRv4GrammarPropertiesStore state) { 29 | mySettings = state; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/org/antlr/intellij/plugin/configdialogs/ANTLRv4ProjectSettings.java: -------------------------------------------------------------------------------- 1 | package org.antlr.intellij.plugin.configdialogs; 2 | 3 | import com.intellij.openapi.Disposable; 4 | import com.intellij.openapi.options.SearchableConfigurable; 5 | import com.intellij.openapi.project.Project; 6 | import com.intellij.openapi.util.Disposer; 7 | import org.jetbrains.annotations.Nls; 8 | import org.jetbrains.annotations.NotNull; 9 | import org.jetbrains.annotations.Nullable; 10 | 11 | import javax.swing.*; 12 | 13 | import static org.antlr.intellij.plugin.configdialogs.ANTLRv4GrammarPropertiesStore.getGrammarProperties; 14 | 15 | /** 16 | * The UI that allows viewing/modifying default grammar settings for an entire project. 17 | * 18 | * @see ConfigANTLRPerGrammar 19 | */ 20 | public class ANTLRv4ProjectSettings implements SearchableConfigurable, Disposable { 21 | 22 | private ConfigANTLRPerGrammar configurationForm; 23 | 24 | private final Project project; 25 | 26 | public ANTLRv4ProjectSettings(@NotNull Project project) { 27 | this.project = project; 28 | } 29 | 30 | @NotNull 31 | @Override 32 | public String getId() { 33 | return "ANTLR4ProjectSettings"; 34 | } 35 | 36 | @Nullable 37 | @Override 38 | public Runnable enableSearch(String option) { 39 | return null; 40 | } 41 | 42 | @Nls(capitalization = Nls.Capitalization.Title) 43 | @Override 44 | public String getDisplayName() { 45 | return "ANTLR4 Project Settings"; 46 | } 47 | 48 | @Nullable 49 | public String getHelpTopic() { 50 | return "ANTLR4 Project Settings"; 51 | } 52 | 53 | @Nullable 54 | @Override 55 | public JComponent createComponent() { 56 | configurationForm = ConfigANTLRPerGrammar.getProjectSettingsForm(project, ANTLRv4GrammarProperties.PROJECT_SETTINGS_PREFIX); 57 | return configurationForm.createCenterPanel(); 58 | } 59 | 60 | @Override 61 | public boolean isModified() { 62 | ANTLRv4GrammarProperties grammarProperties = getGrammarProperties(project, ANTLRv4GrammarProperties.PROJECT_SETTINGS_PREFIX); 63 | return configurationForm.isModified(grammarProperties); 64 | } 65 | 66 | @Override 67 | public void apply() { 68 | configurationForm.saveValues(project, ANTLRv4GrammarProperties.PROJECT_SETTINGS_PREFIX); 69 | } 70 | 71 | public void reset() { 72 | configurationForm.loadValues(project, ANTLRv4GrammarProperties.PROJECT_SETTINGS_PREFIX); 73 | } 74 | 75 | @Override 76 | public void disposeUIResources() { 77 | Disposer.dispose(configurationForm.getDisposable()); 78 | } 79 | 80 | @Override 81 | public void dispose() { 82 | configurationForm = null; 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/main/java/org/antlr/intellij/plugin/configdialogs/CaseChangingStrategyConverter.java: -------------------------------------------------------------------------------- 1 | package org.antlr.intellij.plugin.configdialogs; 2 | 3 | import com.intellij.util.xmlb.Converter; 4 | import org.antlr.intellij.plugin.parsing.CaseChangingStrategy; 5 | import org.jetbrains.annotations.NotNull; 6 | import org.jetbrains.annotations.Nullable; 7 | 8 | /** 9 | * A converter used to serialize/deserialize a {@code CaseChangingStrategy} to/from 10 | * a String contained in {@code .idea/misc.xml} using the enum's {@code name()}. 11 | */ 12 | public class CaseChangingStrategyConverter extends Converter { 13 | @Nullable 14 | @Override 15 | public CaseChangingStrategy fromString(@NotNull String value) { 16 | try { 17 | return CaseChangingStrategy.valueOf(value); 18 | } catch ( IllegalArgumentException e ) { 19 | return null; 20 | } 21 | } 22 | 23 | @Nullable 24 | @Override 25 | public String toString(@NotNull CaseChangingStrategy value) { 26 | return value.name(); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/org/antlr/intellij/plugin/editor/ANTLRv4BraceMatcher.java: -------------------------------------------------------------------------------- 1 | package org.antlr.intellij.plugin.editor; 2 | 3 | import com.intellij.lang.BracePair; 4 | import com.intellij.lang.PairedBraceMatcher; 5 | import com.intellij.psi.PsiFile; 6 | import com.intellij.psi.tree.IElementType; 7 | import org.jetbrains.annotations.NotNull; 8 | import org.jetbrains.annotations.Nullable; 9 | 10 | import static org.antlr.intellij.plugin.ANTLRv4TokenTypes.getTokenElementType; 11 | import static org.antlr.intellij.plugin.parser.ANTLRv4Lexer.*; 12 | 13 | public class ANTLRv4BraceMatcher implements PairedBraceMatcher { 14 | 15 | @NotNull 16 | @Override 17 | public BracePair[] getPairs() { 18 | return new BracePair[] { 19 | new BracePair(getTokenElementType(LPAREN), getTokenElementType(RPAREN), false), 20 | new BracePair(getTokenElementType(OPTIONS), getTokenElementType(RBRACE), true), 21 | new BracePair(getTokenElementType(TOKENS), getTokenElementType(RBRACE), true), 22 | new BracePair(getTokenElementType(CHANNELS), getTokenElementType(RBRACE), true), 23 | new BracePair(getTokenElementType(BEGIN_ACTION), getTokenElementType(END_ACTION), false), 24 | new BracePair(getTokenElementType(BEGIN_ARGUMENT), getTokenElementType(END_ARGUMENT), false), 25 | new BracePair(getTokenElementType(LT), getTokenElementType(GT), false), 26 | }; 27 | } 28 | 29 | @Override 30 | public boolean isPairedBracesAllowedBeforeType(@NotNull IElementType lbraceType, @Nullable IElementType contextType) { 31 | return true; 32 | } 33 | 34 | @Override 35 | public int getCodeConstructStart(PsiFile file, int openingBraceOffset) { 36 | return openingBraceOffset; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/org/antlr/intellij/plugin/folding/ANTLRv4FoldingSettings.java: -------------------------------------------------------------------------------- 1 | package org.antlr.intellij.plugin.folding; 2 | 3 | /** 4 | * Created by jason on 2/2/15. 5 | */ 6 | public abstract class ANTLRv4FoldingSettings { 7 | //TODO ServiceManager,UI,serialization,etc 8 | private static final ANTLRv4FoldingSettings INSTANCE= new ANTLRv4FoldingSettings() { 9 | @Override 10 | public boolean isCollapseFileHeader() { 11 | return true; 12 | } 13 | 14 | @Override 15 | public boolean isCollapseDocComments() { 16 | return false; 17 | } 18 | 19 | @Override 20 | public boolean isCollapseComments() { 21 | return false; 22 | } 23 | 24 | @Override 25 | public boolean isCollapseRuleBlocks() { 26 | return false; 27 | } 28 | 29 | @Override 30 | public boolean isCollapseActions() { 31 | return true; 32 | } 33 | 34 | @Override 35 | public boolean isCollapseTokens() { 36 | return true; 37 | } 38 | }; 39 | 40 | public static ANTLRv4FoldingSettings getInstance() { 41 | return INSTANCE; 42 | } 43 | 44 | public abstract boolean isCollapseFileHeader(); 45 | 46 | public abstract boolean isCollapseDocComments(); 47 | 48 | public abstract boolean isCollapseComments(); 49 | 50 | public abstract boolean isCollapseRuleBlocks(); 51 | 52 | public abstract boolean isCollapseActions(); 53 | 54 | public abstract boolean isCollapseTokens(); 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/org/antlr/intellij/plugin/generators/LiteralChooserObject.java: -------------------------------------------------------------------------------- 1 | package org.antlr.intellij.plugin.generators; 2 | 3 | import com.intellij.ui.SimpleColoredComponent; 4 | import com.intellij.ui.SimpleTextAttributes; 5 | import org.jetbrains.annotations.Nullable; 6 | 7 | import javax.swing.*; 8 | 9 | public class LiteralChooserObject { 10 | private final String text; 11 | private final Icon icon; 12 | 13 | public LiteralChooserObject(final String text, @Nullable final Icon icon) { 14 | this.text = text; 15 | this.icon = icon; 16 | } 17 | 18 | public void renderTreeNode(SimpleColoredComponent component, JTree tree) { 19 | String literal = getText(); 20 | SimpleTextAttributes attributes = 21 | new SimpleTextAttributes(SimpleTextAttributes.STYLE_PLAIN, tree.getForeground()); 22 | component.append(literal, attributes); 23 | component.setIcon(icon); 24 | } 25 | 26 | public String getText() { 27 | return text; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/org/antlr/intellij/plugin/generators/LiteralChooserRenderer.java: -------------------------------------------------------------------------------- 1 | package org.antlr.intellij.plugin.generators; 2 | 3 | import com.intellij.ui.CheckboxTreeBase; 4 | 5 | import javax.swing.*; 6 | import javax.swing.tree.DefaultMutableTreeNode; 7 | 8 | public class LiteralChooserRenderer extends CheckboxTreeBase.CheckboxTreeCellRendererBase { 9 | @Override 10 | public void customizeRenderer(JTree tree, 11 | Object value, 12 | boolean selected, 13 | boolean expanded, 14 | boolean leaf, 15 | int row, 16 | boolean hasFocus) 17 | { 18 | if (value instanceof DefaultMutableTreeNode) { 19 | Object userObject = ((DefaultMutableTreeNode) value).getUserObject(); 20 | if (userObject instanceof LiteralChooserObject) { 21 | LiteralChooserObject literalChooserObject = (LiteralChooserObject)userObject; 22 | literalChooserObject.renderTreeNode(getTextRenderer(), tree); 23 | } 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/org/antlr/intellij/plugin/parsing/CaseChangingCharStream.java: -------------------------------------------------------------------------------- 1 | package org.antlr.intellij.plugin.parsing; 2 | 3 | import org.antlr.v4.runtime.CharStream; 4 | import org.antlr.v4.runtime.misc.Interval; 5 | 6 | /** 7 | * This class supports case-insensitive lexing by wrapping an existing 8 | * {@link CharStream} and forcing the lexer to see either upper or 9 | * lowercase characters. Grammar literals should then be either upper or 10 | * lower case such as 'BEGIN' or 'begin'. The text of the character 11 | * stream is unaffected. Example: input 'BeGiN' would match lexer rule 12 | * 'BEGIN' if constructor parameter upper=true but getText() would return 13 | * 'BeGiN'. 14 | */ 15 | public class CaseChangingCharStream implements CharStream { 16 | 17 | final CharStream stream; 18 | final boolean upper; 19 | 20 | /** 21 | * Constructs a new CaseChangingCharStream wrapping the given {@link CharStream} forcing 22 | * all characters to upper case or lower case. 23 | * @param stream The stream to wrap. 24 | * @param upper If true force each symbol to upper case, otherwise force to lower. 25 | */ 26 | public CaseChangingCharStream(CharStream stream, boolean upper) { 27 | this.stream = stream; 28 | this.upper = upper; 29 | } 30 | 31 | @Override 32 | public String getText(Interval interval) { 33 | return stream.getText(interval); 34 | } 35 | 36 | @Override 37 | public void consume() { 38 | stream.consume(); 39 | } 40 | 41 | @Override 42 | public int LA(int i) { 43 | int c = stream.LA(i); 44 | if (c <= 0) { 45 | return c; 46 | } 47 | if (upper) { 48 | return Character.toUpperCase(c); 49 | } 50 | return Character.toLowerCase(c); 51 | } 52 | 53 | @Override 54 | public int mark() { 55 | return stream.mark(); 56 | } 57 | 58 | @Override 59 | public void release(int marker) { 60 | stream.release(marker); 61 | } 62 | 63 | @Override 64 | public int index() { 65 | return stream.index(); 66 | } 67 | 68 | @Override 69 | public void seek(int index) { 70 | stream.seek(index); 71 | } 72 | 73 | @Override 74 | public int size() { 75 | return stream.size(); 76 | } 77 | 78 | @Override 79 | public String getSourceName() { 80 | return stream.getSourceName(); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/main/java/org/antlr/intellij/plugin/parsing/CaseChangingStrategy.java: -------------------------------------------------------------------------------- 1 | package org.antlr.intellij.plugin.parsing; 2 | 3 | import org.antlr.v4.runtime.CharStream; 4 | 5 | /** 6 | * All the case transformations that can be applied in the ANTLR Preview 7 | * window when lexing the input. 8 | * 9 | * @see CaseChangingCharStream 10 | */ 11 | public enum CaseChangingStrategy { 12 | LEAVE_AS_IS { 13 | @Override 14 | public CharStream applyTo(CharStream source) { 15 | return source; 16 | } 17 | 18 | @Override 19 | public String toString() { 20 | return "Leave as-is"; 21 | } 22 | }, 23 | FORCE_UPPERCASE { 24 | @Override 25 | public CharStream applyTo(CharStream source) { 26 | return new CaseChangingCharStream(source, true); 27 | } 28 | 29 | @Override 30 | public String toString() { 31 | return "Transform to uppercase when lexing"; 32 | } 33 | }, 34 | FORCE_LOWERCASE { 35 | @Override 36 | public CharStream applyTo(CharStream source) { 37 | return new CaseChangingCharStream(source, false); 38 | } 39 | 40 | @Override 41 | public String toString() { 42 | return "Transform to lowercase when lexing"; 43 | } 44 | }; 45 | 46 | public abstract CharStream applyTo(CharStream source); 47 | 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/org/antlr/intellij/plugin/parsing/LexerWatchdog.java: -------------------------------------------------------------------------------- 1 | package org.antlr.intellij.plugin.parsing; 2 | 3 | import org.antlr.v4.runtime.RecognitionException; 4 | import org.antlr.v4.runtime.Token; 5 | import org.antlr.v4.runtime.TokenStream; 6 | 7 | /** 8 | * Checks that a lexer is not stuck trying to match the same thing over and over, for example if the input grammar 9 | * can match an empty string. This prevents the IDE from crashing from things like {@link OutOfMemoryError}s. 10 | */ 11 | public class LexerWatchdog { 12 | 13 | /** 14 | * The number of iterations on the same index after which we kill the interpreter. 15 | */ 16 | private static final int THRESHOLD = 50; 17 | 18 | private final TokenStream tokenStream; 19 | private final PreviewParser previewParser; 20 | 21 | private int currentIndex = -1; 22 | private int iterationsOnCurrentIndex = 0; 23 | 24 | public LexerWatchdog(TokenStream tokenStream, PreviewParser previewParser) { 25 | this.tokenStream = tokenStream; 26 | this.previewParser = previewParser; 27 | } 28 | 29 | public void checkLexerIsNotStuck() { 30 | if ( currentIndex==tokenStream.index() ) { 31 | iterationsOnCurrentIndex++; 32 | } 33 | else { 34 | currentIndex = tokenStream.index(); 35 | iterationsOnCurrentIndex = 1; 36 | } 37 | 38 | if ( iterationsOnCurrentIndex>THRESHOLD ) { 39 | final Token token = tokenStream.get(currentIndex); 40 | final String displayName = token.getType() == Token.EOF 41 | ? token.getText() 42 | : previewParser.getVocabulary().getDisplayName(token.getType()); 43 | 44 | throw new RecognitionException( 45 | "interpreter was killed after " + THRESHOLD + " iterations on token '" + displayName + "'", 46 | previewParser, 47 | tokenStream, 48 | previewParser.getContext() 49 | ) { 50 | @Override 51 | public Token getOffendingToken() { 52 | return token; 53 | } 54 | }; 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/main/java/org/antlr/intellij/plugin/parsing/LoadGrammarsToolListener.java: -------------------------------------------------------------------------------- 1 | package org.antlr.intellij.plugin.parsing; 2 | 3 | import org.antlr.v4.Tool; 4 | import org.antlr.v4.tool.ANTLRMessage; 5 | import org.antlr.v4.tool.DefaultToolListener; 6 | import org.stringtemplate.v4.ST; 7 | 8 | import java.util.ArrayList; 9 | import java.util.List; 10 | 11 | /** Track errors, warnings from loading grammars. Really just 12 | * swallows them for now. The external annotator shows errors. 13 | */ 14 | public class LoadGrammarsToolListener extends DefaultToolListener { 15 | public List grammarErrorMessages = new ArrayList<>(); 16 | public List grammarWarningMessages = new ArrayList<>(); 17 | public LoadGrammarsToolListener(Tool tool) { super(tool); } 18 | 19 | @Override 20 | public void error(ANTLRMessage msg) { 21 | ST msgST = tool.errMgr.getMessageTemplate(msg); 22 | String s = msgST.render(); 23 | if (tool.errMgr.formatWantsSingleLineMessage()) { 24 | s = s.replace('\n', ' '); 25 | } 26 | grammarErrorMessages.add(s); 27 | } 28 | 29 | @Override 30 | public void warning(ANTLRMessage msg) { 31 | ST msgST = tool.errMgr.getMessageTemplate(msg); 32 | String s = msgST.render(); 33 | if (tool.errMgr.formatWantsSingleLineMessage()) { 34 | s = s.replace('\n', ' '); 35 | } 36 | grammarWarningMessages.add(s); 37 | } 38 | 39 | public void clear() { 40 | grammarErrorMessages.clear(); 41 | grammarWarningMessages.clear(); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/org/antlr/intellij/plugin/parsing/ParsingResult.java: -------------------------------------------------------------------------------- 1 | package org.antlr.intellij.plugin.parsing; 2 | 3 | import org.antlr.intellij.adaptor.parser.SyntaxErrorListener; 4 | import org.antlr.v4.runtime.Parser; 5 | import org.antlr.v4.runtime.tree.ParseTree; 6 | 7 | public class ParsingResult { 8 | public Parser parser; 9 | public ParseTree tree; 10 | public SyntaxErrorListener syntaxErrorListener; 11 | 12 | public ParsingResult(Parser parser, ParseTree tree, SyntaxErrorListener syntaxErrorListener) { 13 | this.parser = parser; 14 | this.tree = tree; 15 | this.syntaxErrorListener = syntaxErrorListener; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/org/antlr/intellij/plugin/parsing/PreviewInterpreterRuleContext.java: -------------------------------------------------------------------------------- 1 | package org.antlr.intellij.plugin.parsing; 2 | 3 | import org.antlr.v4.runtime.InterpreterRuleContext; 4 | import org.antlr.v4.runtime.ParserRuleContext; 5 | import org.antlr.v4.tool.GrammarInterpreterRuleContext; 6 | 7 | /** 8 | * This class extends {@link InterpreterRuleContext} to track 9 | * which outermost alternative was used to recognize 10 | * the subphrase matched by the entire rule. 11 | */ 12 | public class PreviewInterpreterRuleContext extends GrammarInterpreterRuleContext { 13 | /** Used to mark root of subtree that hits the decision override, if any */ 14 | protected boolean isDecisionOverrideRoot; 15 | 16 | /** A mark bit used during tree diff walk. If marked, then we reached 17 | * this node. 18 | */ 19 | public boolean reached; 20 | 21 | /** 22 | * Constructs a new {@link InterpreterRuleContext} with the specified 23 | * parent, invoking state, and rule index. 24 | * 25 | * @param parent The parent context. 26 | * @param invokingStateNumber The invoking state number. 27 | * @param ruleIndex The rule index for the current context. 28 | */ 29 | public PreviewInterpreterRuleContext(ParserRuleContext parent, 30 | int invokingStateNumber, 31 | int ruleIndex) 32 | { 33 | super(parent, invokingStateNumber, ruleIndex); 34 | } 35 | 36 | /** The predicted outermost alternative for the rule associated 37 | * with this context object. If left recursive, the true original 38 | * outermost alternative is returned. 39 | */ 40 | public int getOuterAltNum() { return outerAltNum; } 41 | 42 | public boolean isDecisionOverrideRoot() { 43 | return isDecisionOverrideRoot; 44 | } 45 | 46 | @Override 47 | public boolean equals(Object obj) { 48 | if ( !(obj instanceof PreviewInterpreterRuleContext) ) return false; 49 | PreviewInterpreterRuleContext other = (PreviewInterpreterRuleContext) obj; 50 | return this==other || 51 | (ruleIndex == other.ruleIndex && outerAltNum == other.outerAltNum); 52 | } 53 | 54 | @Override 55 | public int hashCode() { 56 | return ruleIndex << 7 + outerAltNum; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/main/java/org/antlr/intellij/plugin/parsing/PreviewParser.java: -------------------------------------------------------------------------------- 1 | package org.antlr.intellij.plugin.parsing; 2 | 3 | import com.intellij.openapi.progress.ProgressManager; 4 | import org.antlr.v4.runtime.*; 5 | import org.antlr.v4.runtime.atn.*; 6 | import org.antlr.v4.tool.Grammar; 7 | import org.antlr.v4.tool.GrammarParserInterpreter; 8 | 9 | import java.util.HashMap; 10 | import java.util.Map; 11 | 12 | public class PreviewParser extends GrammarParserInterpreter { 13 | /** Map each preview editor token to the grammar ATN state used to match it. 14 | * Saves us having to create special token subclass and token factory. 15 | */ 16 | public Map inputTokenToStateMap = new HashMap<>(); 17 | 18 | private final LexerWatchdog lexerWatchdog; 19 | 20 | protected int lastSuccessfulMatchState = ATNState.INVALID_STATE_NUMBER; // not sure about error nodes 21 | 22 | public PreviewParser(Grammar g, ATN atn, TokenStream input) { 23 | super(g, atn, input); 24 | lexerWatchdog = new LexerWatchdog(input, this); 25 | } 26 | 27 | public PreviewParser(Grammar g, TokenStream input) { 28 | this(g, new ATNDeserializer().deserialize(ATNSerializer.getSerialized(g.getATN()).toArray()), input); 29 | } 30 | 31 | @Override 32 | public void reset() { 33 | super.reset(); 34 | if ( inputTokenToStateMap!=null ) inputTokenToStateMap.clear(); 35 | lastSuccessfulMatchState = ATNState.INVALID_STATE_NUMBER; 36 | } 37 | 38 | @Override 39 | protected InterpreterRuleContext createInterpreterRuleContext(ParserRuleContext parent, int invokingStateNumber, int ruleIndex) { 40 | return new PreviewInterpreterRuleContext(parent, invokingStateNumber, ruleIndex); 41 | } 42 | 43 | @Override 44 | protected int visitDecisionState(DecisionState p) { 45 | ProgressManager.checkCanceled(); 46 | 47 | int predictedAlt = super.visitDecisionState(p); 48 | if ( p.getNumberOfTransitions()>1 ) { 49 | if ( p.decision==this.overrideDecision && 50 | this._input.index()==this.overrideDecisionInputIndex ) { 51 | ((PreviewInterpreterRuleContext)overrideDecisionRoot).isDecisionOverrideRoot = true; 52 | } 53 | } 54 | return predictedAlt; 55 | } 56 | 57 | 58 | @Override 59 | public Token match(int ttype) throws RecognitionException { 60 | lexerWatchdog.checkLexerIsNotStuck(); 61 | 62 | Token t = super.match(ttype); 63 | // track which ATN state matches each token 64 | inputTokenToStateMap.put(t, getState()); 65 | lastSuccessfulMatchState = getState(); 66 | return t; 67 | } 68 | 69 | 70 | @Override 71 | public Token matchWildcard() throws RecognitionException { 72 | lexerWatchdog.checkLexerIsNotStuck(); 73 | 74 | inputTokenToStateMap.put(_input.LT(1), getState()); 75 | lastSuccessfulMatchState = getState(); 76 | return super.matchWildcard(); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/main/java/org/antlr/intellij/plugin/parsing/RunANTLRListener.java: -------------------------------------------------------------------------------- 1 | package org.antlr.intellij.plugin.parsing; 2 | 3 | import com.intellij.execution.ui.ConsoleView; 4 | import com.intellij.execution.ui.ConsoleViewContentType; 5 | import org.antlr.v4.Tool; 6 | import org.antlr.v4.tool.ANTLRMessage; 7 | import org.antlr.v4.tool.ANTLRToolListener; 8 | import org.stringtemplate.v4.ST; 9 | 10 | import java.util.ArrayList; 11 | import java.util.List; 12 | 13 | /** Used to track errors during antlr run on a grammar for generation, 14 | * not for annotation of grammar. 15 | */ 16 | public class RunANTLRListener implements ANTLRToolListener { 17 | public final List all = new ArrayList<>(); 18 | public Tool tool; 19 | public ConsoleView console; 20 | public boolean hasOutput = false; 21 | 22 | public RunANTLRListener(Tool tool, ConsoleView console) { 23 | this.tool = tool; 24 | this.console = console; 25 | } 26 | 27 | @Override 28 | public void info(String msg) { 29 | if (tool.errMgr.formatWantsSingleLineMessage()) { 30 | msg = msg.replace('\n', ' '); 31 | } 32 | console.print(msg+"\n", ConsoleViewContentType.NORMAL_OUTPUT); 33 | hasOutput = true; 34 | } 35 | 36 | @Override 37 | public void error(ANTLRMessage msg) { 38 | track(msg, ConsoleViewContentType.ERROR_OUTPUT); 39 | } 40 | 41 | @Override 42 | public void warning(ANTLRMessage msg) { 43 | track(msg, ConsoleViewContentType.NORMAL_OUTPUT); 44 | } 45 | 46 | private void track(ANTLRMessage msg, ConsoleViewContentType errType) { 47 | ST msgST = tool.errMgr.getMessageTemplate(msg); 48 | String outputMsg = msgST.render(); 49 | if (tool.errMgr.formatWantsSingleLineMessage()) { 50 | outputMsg = outputMsg.replace('\n', ' '); 51 | } 52 | console.print(outputMsg+"\n", errType); 53 | hasOutput = true; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/org/antlr/intellij/plugin/parsing/TokenStreamSubset.java: -------------------------------------------------------------------------------- 1 | package org.antlr.intellij.plugin.parsing; 2 | 3 | import org.antlr.v4.runtime.CommonToken; 4 | import org.antlr.v4.runtime.CommonTokenStream; 5 | import org.antlr.v4.runtime.Token; 6 | import org.antlr.v4.runtime.TokenSource; 7 | 8 | /** This TokenStream is just a {@link CommonTokenStream} that can be 9 | * cut off at a particular index, such as the cursor in an IDE. I 10 | * had to override more than I wanted to get this to work, but it seems okay. 11 | * 12 | * All parsers used within the plug-in should use token streams of this type. 13 | */ 14 | public class TokenStreamSubset extends CommonTokenStream { 15 | public static final int STOP_TOKEN_TYPE = -3; 16 | // protected int indexOfLastToken = -1; 17 | protected Token saveToken; 18 | 19 | public TokenStreamSubset(TokenSource tokenSource) { 20 | super(tokenSource); 21 | } 22 | 23 | public void setIndexOfLastToken(int indexOfLastToken) { 24 | // System.out.println("setIndexOfLastToken("+indexOfLastToken+")"); 25 | if ( indexOfLastToken<0 ) { 26 | // System.out.println("replacing "+saveToken.getTokenIndex()+" with "+saveToken); 27 | tokens.set(saveToken.getTokenIndex(), saveToken); 28 | // this.indexOfLastToken = indexOfLastToken; 29 | return; 30 | } 31 | int i = indexOfLastToken + 1; // we want to keep token at indexOfLastToken 32 | sync(i); 33 | saveToken = tokens.get(i); 34 | // System.out.println("saving "+saveToken); 35 | CommonToken stopToken = new CommonToken(saveToken); 36 | stopToken.setType(STOP_TOKEN_TYPE); 37 | // System.out.println("setting "+i+" to "+stopToken); 38 | tokens.set(i, stopToken); 39 | // this.indexOfLastToken = indexOfLastToken; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/org/antlr/intellij/plugin/preview/AltLabelTextProvider.java: -------------------------------------------------------------------------------- 1 | package org.antlr.intellij.plugin.preview; 2 | 3 | import org.antlr.intellij.plugin.parsing.PreviewInterpreterRuleContext; 4 | import org.antlr.v4.gui.TreeTextProvider; 5 | import org.antlr.v4.runtime.Parser; 6 | import org.antlr.v4.runtime.Token; 7 | import org.antlr.v4.runtime.misc.Pair; 8 | import org.antlr.v4.runtime.tree.TerminalNode; 9 | import org.antlr.v4.runtime.tree.Tree; 10 | import org.antlr.v4.runtime.tree.Trees; 11 | import org.antlr.v4.tool.Grammar; 12 | import org.antlr.v4.tool.Rule; 13 | import org.antlr.v4.tool.ast.AltAST; 14 | 15 | import java.util.Arrays; 16 | import java.util.List; 17 | import java.util.Map; 18 | 19 | public class AltLabelTextProvider implements TreeTextProvider { 20 | protected final Parser parser; 21 | protected final Grammar g; 22 | 23 | public AltLabelTextProvider(Parser parser, Grammar g) { 24 | this.parser = parser; 25 | this.g = g; 26 | } 27 | 28 | public String[] getAltLabels(Rule r) { 29 | String[] altLabels = null; 30 | Map>> altLabelsMap = r.getAltLabels(); 31 | if ( altLabelsMap!=null ) { 32 | altLabels = new String[r.getOriginalNumberOfAlts() + 1]; 33 | for (String altLabel : altLabelsMap.keySet()) { 34 | List> pairs = altLabelsMap.get(altLabel); 35 | for (Pair pair : pairs) { 36 | altLabels[pair.a] = altLabel; 37 | } 38 | } 39 | } 40 | return altLabels; 41 | } 42 | 43 | @Override 44 | public String getText(Tree node) { 45 | if ( node instanceof PreviewInterpreterRuleContext) { 46 | PreviewInterpreterRuleContext inode = (PreviewInterpreterRuleContext)node; 47 | Rule r = g.getRule(inode.getRuleIndex()); 48 | String[] altLabels = getAltLabels(r); 49 | String name = r.name; 50 | int outerAltNum = inode.getOuterAltNum(); 51 | if ( altLabels!=null ) { 52 | if ( outerAltNum>=0 && outerAltNum1 ) { 60 | return name + ":" +outerAltNum; 61 | } 62 | else { 63 | return name; // don't display an alternative number if there's only one 64 | } 65 | } 66 | else if (node instanceof TerminalNode) { 67 | return getLabelForToken( ((TerminalNode)node).getSymbol() ); 68 | } 69 | return Trees.getNodeText(node, Arrays.asList(parser.getRuleNames())); 70 | } 71 | 72 | private String getLabelForToken(Token token) { 73 | String text = token.getText(); 74 | if (text.equals("")) { 75 | return text; 76 | } 77 | 78 | String symbolicName = parser.getVocabulary().getSymbolicName(token.getType()); 79 | if ( symbolicName==null ) { // it's a literal like ';' or 'return' 80 | return text; 81 | } 82 | if ( text.toUpperCase().equals(symbolicName) ) { // IMPORT:import 83 | return symbolicName; 84 | } 85 | return symbolicName + ":" + text; 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/main/java/org/antlr/intellij/plugin/preview/CancelParserAction.java: -------------------------------------------------------------------------------- 1 | package org.antlr.intellij.plugin.preview; 2 | 3 | import com.intellij.icons.AllIcons; 4 | import com.intellij.openapi.actionSystem.ActionUpdateThread; 5 | import com.intellij.openapi.actionSystem.AnAction; 6 | import com.intellij.openapi.actionSystem.AnActionEvent; 7 | import org.antlr.intellij.plugin.ANTLRv4PluginController; 8 | import org.jetbrains.annotations.NotNull; 9 | 10 | /** 11 | * A button that allows the user to kill the interpreter if it's taking too long to 12 | * process the input in the Preview window. 13 | */ 14 | public class CancelParserAction extends AnAction { 15 | 16 | private boolean enabled = false; 17 | 18 | public CancelParserAction() { 19 | super("Cancel Parsing", "Cancel the current parsing", AllIcons.Actions.Suspend); 20 | } 21 | 22 | @Override 23 | public void update(@NotNull AnActionEvent e) { 24 | super.update(e); 25 | 26 | e.getPresentation().setEnabled(enabled); 27 | } 28 | 29 | @Override 30 | public @NotNull ActionUpdateThread getActionUpdateThread() { 31 | return ActionUpdateThread.EDT; 32 | } 33 | 34 | @Override 35 | public void actionPerformed(@NotNull AnActionEvent e) { 36 | final ANTLRv4PluginController controller = ANTLRv4PluginController.getInstance(e.getProject()); 37 | 38 | if (controller != null) { 39 | controller.abortCurrentParsing(); 40 | } 41 | } 42 | 43 | public void setEnabled(boolean enabled) { 44 | this.enabled = enabled; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/org/antlr/intellij/plugin/preview/InputPanel.form: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 |
85 | -------------------------------------------------------------------------------- /src/main/java/org/antlr/intellij/plugin/preview/ParsingResultSelectionListener.java: -------------------------------------------------------------------------------- 1 | package org.antlr.intellij.plugin.preview; 2 | 3 | import org.antlr.v4.runtime.tree.Tree; 4 | 5 | /** 6 | * Fired when a parsing result element (token or matched rule) 7 | * is selected in one of the viewers. Used to highlight the 8 | * corresponding area in the input editor. 9 | */ 10 | public interface ParsingResultSelectionListener { 11 | void onParserRuleSelected(Tree rule); 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/org/antlr/intellij/plugin/preview/PreviewState.java: -------------------------------------------------------------------------------- 1 | package org.antlr.intellij.plugin.preview; 2 | 3 | import com.intellij.openapi.application.ApplicationManager; 4 | import com.intellij.openapi.editor.Editor; 5 | import com.intellij.openapi.editor.EditorFactory; 6 | import com.intellij.openapi.project.Project; 7 | import com.intellij.openapi.vfs.VirtualFile; 8 | import org.antlr.intellij.plugin.parsing.ParsingResult; 9 | import org.antlr.v4.tool.Grammar; 10 | import org.antlr.v4.tool.LexerGrammar; 11 | 12 | /** Track everything associated with the state of the preview window. 13 | * For each grammar, we need to track an InputPanel (with <= 2 editor objects) 14 | * that we will flip to every time we come back to a specific grammar, 15 | * uniquely identified by the fully-qualified grammar name. 16 | * 17 | * Before parsing can begin, we need to know the start rule. That means that 18 | * we should not show an editor until this field is filled in. 19 | * 20 | * The plug-in controller should update all of these elements atomically so 21 | * they are self consistent. We must be careful then to send these fields 22 | * around together as a unit instead of asking the controller for the 23 | * elements piecemeal. That could get g and lg for different grammar files, 24 | * for example. 25 | */ 26 | public class PreviewState { 27 | public Project project; 28 | public VirtualFile grammarFile; 29 | public Grammar g; 30 | public LexerGrammar lg; 31 | public String startRuleName; 32 | public CharSequence manualInputText = ""; // save input when switching grammars 33 | public VirtualFile inputFile; // save input file when switching grammars 34 | 35 | public ParsingResult parsingResult; 36 | 37 | /** The current input editor (inputEditor or fileEditor) for this grammar 38 | * in InputPanel. This can be null when a PreviewState and InputPanel 39 | * are created out of sync. Depends on order IDE opens files vs 40 | * creates preview pane. 41 | */ 42 | private Editor inputEditor; 43 | 44 | public PreviewState(Project project, VirtualFile grammarFile) { 45 | this.project = project; 46 | this.grammarFile = grammarFile; 47 | } 48 | 49 | public synchronized Editor getInputEditor() { 50 | return inputEditor; 51 | } 52 | 53 | public synchronized void setInputEditor(Editor inputEditor) { 54 | releaseEditor(); 55 | this.inputEditor = inputEditor; 56 | } 57 | 58 | public Grammar getMainGrammar() { 59 | return g!=null ? g : lg; 60 | } 61 | 62 | public synchronized void releaseEditor() { 63 | 64 | // Editor can't be release during unit tests, because it is used by multiple tests 65 | if (ApplicationManager.getApplication().isUnitTestMode()) return; 66 | 67 | // It would appear that the project closed event occurs before these 68 | // close grammars sometimes. Very strange. check for null editor. 69 | if (inputEditor != null) { 70 | final EditorFactory factory = EditorFactory.getInstance(); 71 | factory.releaseEditor(inputEditor); 72 | inputEditor = null; 73 | } 74 | } 75 | 76 | } 77 | -------------------------------------------------------------------------------- /src/main/java/org/antlr/intellij/plugin/preview/TrackpadZoomingTreeView.java: -------------------------------------------------------------------------------- 1 | package org.antlr.intellij.plugin.preview; 2 | 3 | import com.intellij.ui.components.Magnificator; 4 | import org.antlr.v4.runtime.tree.Tree; 5 | 6 | import javax.swing.*; 7 | import java.awt.*; 8 | 9 | /** 10 | * Created by jason on 2/7/15. 11 | */ 12 | public class TrackpadZoomingTreeView extends UberTreeViewer implements Magnificator { 13 | public TrackpadZoomingTreeView(java.util.List ruleNames, Tree tree, boolean highlightUnreachedNodes) { 14 | super(ruleNames, tree, highlightUnreachedNodes); 15 | //TODO: memory leak? 16 | putClientProperty(Magnificator.CLIENT_PROPERTY_KEY, this); 17 | } 18 | 19 | public final ScaleModel scaleModel = new ScaleModel(1000); 20 | 21 | @Override 22 | public Point magnify(double magnification, Point at) { 23 | double s = getScale(); 24 | scaleModel.setDoubleValue(magnification * s); 25 | return at; 26 | } 27 | 28 | static final double SCALE_MIN = 0.1; 29 | static final double SCALE_MAX = 2.5; 30 | static final double SCALE_RANGE = SCALE_MAX - SCALE_MIN; 31 | 32 | class ScaleModel extends DefaultBoundedRangeModel { 33 | ScaleModel(int ticks) { 34 | super(ticks / 2, 0, 1, ticks); 35 | } 36 | 37 | int range() { 38 | return getMaximum() - getMinimum(); 39 | } 40 | 41 | //TODO: these methods could use caching if performance becomes an issue; 42 | double i2dTranslate(double val) { 43 | return val + (SCALE_MIN - (double) getMinimum()); 44 | } 45 | 46 | double i2dScale(double val) { 47 | return val * (SCALE_RANGE / ((double) range())); 48 | } 49 | 50 | double d2iTranslate(double val) { 51 | return val + (((double) getMinimum()) - SCALE_MIN); 52 | } 53 | 54 | double d2iScale(double val) { 55 | return val * ((double) range()) / SCALE_RANGE; 56 | } 57 | 58 | 59 | int computeIntValue(double doubleValue) { 60 | return Math.round((float) d2iTranslate(d2iScale(doubleValue))); 61 | } 62 | 63 | double computeDoubleValue() { 64 | return i2dScale(i2dTranslate((double) getValue())); 65 | } 66 | 67 | @Override 68 | public void setValue(int i) { 69 | super.setValue(i); 70 | TrackpadZoomingTreeView.this.doSetScale(computeDoubleValue()); 71 | 72 | } 73 | 74 | public void setDoubleValue(double value) { 75 | setValue(computeIntValue(value)); 76 | } 77 | 78 | public double getDoubleValue() { 79 | return TrackpadZoomingTreeView.this.getScale(); 80 | } 81 | 82 | } 83 | 84 | static double clamp(double val) { 85 | if (val <= SCALE_MIN) return SCALE_MIN; 86 | if (val >= SCALE_MAX) return SCALE_MAX; 87 | return val; 88 | } 89 | 90 | void doSetScale(double scale) { 91 | super.setScale(clamp(scale)); 92 | } 93 | 94 | @Override 95 | public void setScale(double scale) { 96 | //route everything through the scale model so that the slider will stay in sync; 97 | scaleModel.setDoubleValue(scale); 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /src/main/java/org/antlr/intellij/plugin/preview/UberTreeViewer.java: -------------------------------------------------------------------------------- 1 | package org.antlr.intellij.plugin.preview; 2 | 3 | import com.intellij.ui.DarculaColors; 4 | import com.intellij.ui.Gray; 5 | import com.intellij.ui.JBColor; 6 | import org.antlr.intellij.plugin.parsing.PreviewInterpreterRuleContext; 7 | import org.antlr.v4.gui.TreeViewer; 8 | import org.antlr.v4.runtime.ParserRuleContext; 9 | import org.antlr.v4.runtime.tree.ErrorNode; 10 | import org.antlr.v4.runtime.tree.Tree; 11 | 12 | import java.awt.*; 13 | import java.awt.geom.Rectangle2D; 14 | import java.util.List; 15 | 16 | public class UberTreeViewer extends TreeViewer { 17 | private final boolean highlightUnreachedNodes; 18 | 19 | public UberTreeViewer(List ruleNames, Tree tree, boolean highlightUnreachedNodes) { 20 | super(ruleNames, tree); 21 | this.highlightUnreachedNodes = highlightUnreachedNodes; 22 | } 23 | 24 | @Override 25 | protected void paintBox(Graphics g, Tree tree) { 26 | customPaintBox(g, tree); 27 | 28 | Rectangle2D.Double box = getBoundsOfNode(tree); 29 | if ( tree instanceof PreviewInterpreterRuleContext ) { 30 | PreviewInterpreterRuleContext ctx = (PreviewInterpreterRuleContext)tree; 31 | if ( highlightUnreachedNodes && !ctx.reached ) { 32 | g.setColor(JBColor.orange); 33 | g.drawRoundRect((int) box.x, (int) box.y, (int) box.width - 1, 34 | (int) box.height - 1, arcSize, arcSize); 35 | } 36 | } 37 | } 38 | 39 | // Customized version of super.paintBox() that supports Darcula colors 40 | private void customPaintBox(Graphics g, Tree tree) { 41 | Rectangle2D.Double box = getBoundsOfNode(tree); 42 | // draw the box in the background 43 | boolean ruleFailedAndMatchedNothing = false; 44 | if ( tree instanceof ParserRuleContext ) { 45 | ParserRuleContext ctx = (ParserRuleContext) tree; 46 | ruleFailedAndMatchedNothing = ctx.exception != null && 47 | ctx.stop != null && ctx.stop.getTokenIndex() < ctx.start.getTokenIndex(); 48 | } 49 | if ( isHighlighted(tree) || boxColor!=null || 50 | tree instanceof ErrorNode || 51 | ruleFailedAndMatchedNothing) 52 | { 53 | if ( isHighlighted(tree) ) g.setColor(highlightedBoxColor); 54 | else if ( tree instanceof ErrorNode || ruleFailedAndMatchedNothing ) g.setColor(DarculaColors.RED); 55 | else g.setColor(boxColor); 56 | g.fillRoundRect((int) box.x, (int) box.y, (int) box.width, 57 | (int) box.height, arcSize, arcSize); 58 | } 59 | if ( borderColor!=null ) { 60 | g.setColor(borderColor); 61 | g.drawRoundRect((int) box.x, (int) box.y, (int) box.width, 62 | (int) box.height, arcSize, arcSize); 63 | } 64 | 65 | // draw the text on top of the box (possibly multiple lines) 66 | if ( tree instanceof ErrorNode || ruleFailedAndMatchedNothing ) { 67 | g.setColor(Gray._64); 68 | } 69 | else { 70 | g.setColor(textColor); 71 | } 72 | String s = getText(tree); 73 | String[] lines = s.split("\n"); 74 | FontMetrics m = getFontMetrics(font); 75 | int x = (int) box.x + arcSize / 2 + nodeWidthPadding; 76 | int y = (int) box.y + m.getAscent() + m.getLeading() + 1 + nodeHeightPadding; 77 | for (int i = 0; i < lines.length; i++) { 78 | text(g, lines[i], x, y); 79 | y += m.getHeight(); 80 | } 81 | } 82 | 83 | @Override 84 | public void setTree(Tree root) { 85 | setTextColor(JBColor.BLACK); 86 | super.setTree(root); 87 | } 88 | 89 | public boolean hasTree() { 90 | return treeLayout!=null; 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /src/main/java/org/antlr/intellij/plugin/preview/WrappedFlowLayout.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2000-2016 JetBrains s.r.o. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.antlr.intellij.plugin.preview; 17 | 18 | import javax.swing.*; 19 | import java.awt.*; 20 | 21 | public class WrappedFlowLayout extends FlowLayout { 22 | 23 | public WrappedFlowLayout(int hGap, int vGap) { 24 | super(FlowLayout.LEADING, hGap, vGap); 25 | } 26 | 27 | @Override 28 | public Dimension preferredLayoutSize(Container target) { 29 | Dimension baseSize = super.preferredLayoutSize(target); 30 | if (getAlignOnBaseline()) { 31 | return baseSize; 32 | } 33 | 34 | return getWrappedSize(target); 35 | } 36 | 37 | public Dimension getWrappedSize(Container target) { 38 | Container parent = SwingUtilities.getUnwrappedParent(target); 39 | int maxWidth = parent.getWidth() - (parent.getInsets().left + parent.getInsets().right); 40 | 41 | return getDimension(target, maxWidth); 42 | } 43 | 44 | public Dimension getDimension(Container target, int maxWidth) { 45 | Insets insets = target.getInsets(); 46 | int height = insets.top + insets.bottom; 47 | int width = insets.left + insets.right; 48 | 49 | int rowHeight = 0; 50 | int rowWidth = insets.left + insets.right; 51 | 52 | boolean isVisible = false; 53 | boolean start = true; 54 | 55 | synchronized (target.getTreeLock()) { 56 | for (int i = 0; i < target.getComponentCount(); i++) { 57 | Component component = target.getComponent(i); 58 | if (component.isVisible()) { 59 | isVisible = true; 60 | Dimension size = component.getPreferredSize(); 61 | 62 | if (rowWidth + getHgap() + size.width > maxWidth && !start) { 63 | height += getVgap() + rowHeight; 64 | width = Math.max(width, rowWidth); 65 | 66 | rowWidth = insets.left + insets.right; 67 | rowHeight = 0; 68 | } 69 | 70 | rowWidth += getHgap() + size.width; 71 | rowHeight = Math.max(rowHeight, size.height); 72 | 73 | start = false; 74 | } 75 | } 76 | height += getVgap() + rowHeight; 77 | width = Math.max(width, rowWidth); 78 | 79 | if (!isVisible) { 80 | return super.preferredLayoutSize(target); 81 | } 82 | else { 83 | return new Dimension(width, height); 84 | } 85 | } 86 | } 87 | 88 | @Override 89 | public Dimension minimumLayoutSize(Container target) { 90 | if (getAlignOnBaseline()) return super.minimumLayoutSize(target); 91 | 92 | return getWrappedSize(target); 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /src/main/java/org/antlr/intellij/plugin/profiler/ProfilerTableDataModel.java: -------------------------------------------------------------------------------- 1 | package org.antlr.intellij.plugin.profiler; 2 | 3 | import javax.swing.table.AbstractTableModel; 4 | 5 | public abstract class ProfilerTableDataModel extends AbstractTableModel { 6 | 7 | public abstract String[] getColumnNames(); 8 | public abstract String[] getColumnToolTips(); 9 | 10 | @Override 11 | public String getColumnName(int column) { 12 | return getColumnNames()[column]; 13 | } 14 | 15 | @Override 16 | public Class getColumnClass(int columnIndex) { 17 | return Integer.class; 18 | } 19 | 20 | @Override 21 | public int getColumnCount() { 22 | return getColumnNames().length; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/org/antlr/intellij/plugin/profiler/SimpleProfilerTableDataModel.java: -------------------------------------------------------------------------------- 1 | package org.antlr.intellij.plugin.profiler; 2 | 3 | import org.antlr.v4.runtime.Parser; 4 | import org.antlr.v4.runtime.atn.DecisionInfo; 5 | import org.antlr.v4.runtime.atn.ParseInfo; 6 | 7 | import java.math.BigDecimal; 8 | import java.math.RoundingMode; 9 | import java.util.LinkedHashMap; 10 | 11 | public class SimpleProfilerTableDataModel extends ProfilerTableDataModel { 12 | public ParseInfo parseInfo; 13 | public LinkedHashMap nameToColumnMap = new LinkedHashMap<>(); 14 | public static final String[] columnNames = { 15 | "Rule","Invocations", "Time (ms)", "Total k", "Max k", "Ambiguities", "DFA cache miss" 16 | }; 17 | 18 | public static final String[] columnToolTips = { 19 | "name of rule and decision no", 20 | "# decision invocations", 21 | "Rough estimate of time (ms) spent in prediction", 22 | "Total lookahead symbols examined", 23 | "Max lookahead symbols examined in any decision event", 24 | "# of ambiguous input phrases", 25 | "# of non-DFA transitions during prediction (cache miss)" 26 | }; 27 | 28 | private final String[] ruleNamesByDecision ; 29 | public SimpleProfilerTableDataModel(ParseInfo parseInfo,Parser parser) { 30 | this.parseInfo = parseInfo; 31 | /*copying rule names to not hold ref to parser object*/ 32 | ruleNamesByDecision = new String[parser.getATN().decisionToState.size()]; 33 | for(int i = 0; i < ruleNamesByDecision .length; i++) { 34 | ruleNamesByDecision [i] = parser.getRuleNames()[parser.getATN().getDecisionState(i).ruleIndex]; 35 | } 36 | for (int i = 0; i < columnNames.length; i++) { 37 | nameToColumnMap.put(columnNames[i], i); 38 | } 39 | } 40 | 41 | @Override 42 | public String[] getColumnNames() { 43 | return columnNames; 44 | } 45 | 46 | @Override 47 | public String[] getColumnToolTips() { 48 | return columnToolTips; 49 | } 50 | 51 | @Override 52 | public int getRowCount() { 53 | return parseInfo.getDecisionInfo().length; 54 | } 55 | 56 | @Override 57 | public Object getValueAt(int row, int col) { 58 | int decision = row; 59 | DecisionInfo decisionInfo = parseInfo.getDecisionInfo()[decision]; 60 | switch (col) { // laborious but more efficient than reflection 61 | case 0: 62 | return String.format("%s (%d)",ruleNamesByDecision [decision],decision); 63 | case 1: 64 | return decisionInfo.invocations; 65 | case 2: 66 | return BigDecimal.valueOf(decisionInfo.timeInPrediction / (1000.0 * 1000.0)).setScale(3, RoundingMode.HALF_DOWN); 67 | case 3: 68 | return decisionInfo.LL_TotalLook+decisionInfo.SLL_TotalLook; 69 | case 4: 70 | return Math.max(decisionInfo.LL_MaxLook, decisionInfo.SLL_MaxLook); 71 | case 5: 72 | return decisionInfo.ambiguities.size(); 73 | case 6: 74 | return decisionInfo.SLL_ATNTransitions+ 75 | decisionInfo.LL_ATNTransitions; 76 | } 77 | return "n/a"; 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/main/java/org/antlr/intellij/plugin/psi/ANTLRv4IndexPatternBuilder.java: -------------------------------------------------------------------------------- 1 | package org.antlr.intellij.plugin.psi; 2 | 3 | import com.intellij.lexer.Lexer; 4 | import com.intellij.psi.PsiFile; 5 | import com.intellij.psi.impl.search.IndexPatternBuilder; 6 | import com.intellij.psi.tree.IElementType; 7 | import com.intellij.psi.tree.TokenSet; 8 | import org.antlr.intellij.plugin.ANTLRv4FileRoot; 9 | import org.antlr.intellij.plugin.adaptors.ANTLRv4LexerAdaptor; 10 | import org.antlr.intellij.plugin.parser.ANTLRv4Lexer; 11 | import org.jetbrains.annotations.NotNull; 12 | import org.jetbrains.annotations.Nullable; 13 | 14 | import static org.antlr.intellij.plugin.ANTLRv4TokenTypes.getTokenElementType; 15 | 16 | public class ANTLRv4IndexPatternBuilder implements IndexPatternBuilder { 17 | 18 | @Nullable 19 | @Override 20 | public Lexer getIndexingLexer(@NotNull PsiFile file) { 21 | if ( file instanceof ANTLRv4FileRoot ) { 22 | ANTLRv4Lexer lexer = new ANTLRv4Lexer(null); 23 | return new ANTLRv4LexerAdaptor(lexer); 24 | } 25 | return null; 26 | } 27 | 28 | @Nullable 29 | @Override 30 | public TokenSet getCommentTokenSet(@NotNull PsiFile file) { 31 | if ( file instanceof ANTLRv4FileRoot ) { 32 | return TokenSet.create(getTokenElementType(ANTLRv4Lexer.LINE_COMMENT)); 33 | } 34 | return null; 35 | } 36 | 37 | @Override 38 | public int getCommentStartDelta(IElementType tokenType) { 39 | return tokenType==getTokenElementType(ANTLRv4Lexer.LINE_COMMENT) ? 2 : 0; 40 | } 41 | 42 | @Override 43 | public int getCommentEndDelta(IElementType tokenType) { 44 | return 0; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/org/antlr/intellij/plugin/psi/AtAction.java: -------------------------------------------------------------------------------- 1 | package org.antlr.intellij.plugin.psi; 2 | 3 | import com.intellij.extapi.psi.ASTWrapperPsiElement; 4 | import com.intellij.lang.ASTNode; 5 | import com.intellij.psi.PsiElement; 6 | import org.antlr.intellij.adaptor.parser.PsiElementFactory; 7 | import org.antlr.intellij.plugin.ANTLRv4TokenTypes; 8 | import org.antlr.intellij.plugin.parser.ANTLRv4Parser; 9 | import org.jetbrains.annotations.NotNull; 10 | 11 | public class AtAction extends ASTWrapperPsiElement { 12 | public AtAction(@NotNull ASTNode node) { 13 | super(node); 14 | } 15 | 16 | @NotNull 17 | public String getIdText() { 18 | PsiElement id = findChildByType(ANTLRv4TokenTypes.getRuleElementType(ANTLRv4Parser.RULE_identifier)); 19 | 20 | return id==null ? "" : id.getText(); 21 | } 22 | 23 | @NotNull 24 | public String getActionBlockText() { 25 | PsiElement actionBlock = findChildByType(ANTLRv4TokenTypes.getRuleElementType(ANTLRv4Parser.RULE_actionBlock)); 26 | 27 | if (actionBlock != null) { 28 | PsiElement openingBrace = actionBlock.getFirstChild(); 29 | PsiElement closingBrace = actionBlock.getLastChild(); 30 | 31 | return actionBlock.getText().substring(openingBrace.getStartOffsetInParent() + 1, closingBrace.getStartOffsetInParent()); 32 | } 33 | 34 | return ""; 35 | } 36 | 37 | public static class Factory implements PsiElementFactory { 38 | public static Factory INSTANCE = new Factory(); 39 | 40 | @Override 41 | public PsiElement createElement(ASTNode node) { 42 | return new AtAction(node); 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/org/antlr/intellij/plugin/psi/ChannelSpecNode.java: -------------------------------------------------------------------------------- 1 | package org.antlr.intellij.plugin.psi; 2 | 3 | import com.intellij.lang.ASTNode; 4 | import com.intellij.psi.tree.IElementType; 5 | import com.intellij.psi.util.PsiTreeUtil; 6 | import org.antlr.intellij.plugin.ANTLRv4TokenTypes; 7 | import org.antlr.intellij.plugin.parser.ANTLRv4Lexer; 8 | import org.jetbrains.annotations.NotNull; 9 | 10 | /** 11 | * A channel defined in the {@code channels} section. 12 | */ 13 | public class ChannelSpecNode extends RuleSpecNode { 14 | 15 | public ChannelSpecNode(@NotNull ASTNode node) { 16 | super(node); 17 | } 18 | 19 | @Override 20 | public GrammarElementRefNode getNameIdentifier() { 21 | return PsiTreeUtil.getChildOfType(this, LexerRuleRefNode.class); 22 | } 23 | 24 | @Override 25 | public IElementType getRuleRefType() { 26 | return ANTLRv4TokenTypes.TOKEN_ELEMENT_TYPES.get(ANTLRv4Lexer.TOKEN_REF); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/org/antlr/intellij/plugin/psi/GrammarElementRef.java: -------------------------------------------------------------------------------- 1 | package org.antlr.intellij.plugin.psi; 2 | 3 | import com.intellij.openapi.project.Project; 4 | import com.intellij.openapi.util.TextRange; 5 | import com.intellij.psi.PsiElement; 6 | import com.intellij.psi.PsiFile; 7 | import com.intellij.psi.PsiReferenceBase; 8 | import com.intellij.psi.util.PsiTreeUtil; 9 | import com.intellij.util.IncorrectOperationException; 10 | import org.antlr.intellij.plugin.ANTLRv4TokenTypes; 11 | import org.antlr.intellij.plugin.parser.ANTLRv4Lexer; 12 | import org.antlr.intellij.plugin.resolve.ImportResolver; 13 | import org.antlr.intellij.plugin.resolve.TokenVocabResolver; 14 | import org.jetbrains.annotations.NotNull; 15 | import org.jetbrains.annotations.Nullable; 16 | 17 | import java.util.Collection; 18 | 19 | /** 20 | * A reference to a grammar element (parser rule, lexer rule or lexical mode). 21 | */ 22 | public class GrammarElementRef extends PsiReferenceBase { 23 | 24 | private final String ruleName; 25 | 26 | public GrammarElementRef(GrammarElementRefNode idNode, String ruleName) { 27 | super(idNode, new TextRange(0, ruleName.length())); 28 | this.ruleName = ruleName; 29 | } 30 | 31 | /** 32 | * Using for completion. Returns list of rules and tokens; the prefix 33 | * of current element is used as filter by IDEA later. 34 | */ 35 | @NotNull 36 | @Override 37 | public Object[] getVariants() { 38 | RulesNode rules = PsiTreeUtil.getContextOfType(myElement, RulesNode.class); 39 | // find all rule defs (token, parser) 40 | Collection ruleSpecNodes = 41 | PsiTreeUtil.findChildrenOfAnyType(rules, ParserRuleSpecNode.class, LexerRuleSpecNode.class); 42 | 43 | return ruleSpecNodes.toArray(); 44 | } 45 | 46 | /** 47 | * Called upon jump to def for this rule ref 48 | */ 49 | @Nullable 50 | @Override 51 | public PsiElement resolve() { 52 | PsiFile tokenVocabFile = TokenVocabResolver.resolveTokenVocabFile(getElement()); 53 | 54 | if ( tokenVocabFile!=null ) { 55 | return tokenVocabFile; 56 | } 57 | 58 | PsiFile importedFile = ImportResolver.resolveImportedFile(getElement()); 59 | if ( importedFile!=null ) { 60 | return importedFile; 61 | } 62 | 63 | GrammarSpecNode grammar = PsiTreeUtil.getContextOfType(getElement(), GrammarSpecNode.class); 64 | PsiElement specNode = MyPsiUtils.findSpecNode(grammar, ruleName); 65 | 66 | if ( specNode!=null ) { 67 | return specNode; 68 | } 69 | 70 | // Look for a rule defined in an imported grammar 71 | specNode = ImportResolver.resolveInImportedFiles(getElement().getContainingFile(), ruleName); 72 | 73 | if ( specNode!=null ) { 74 | return specNode; 75 | } 76 | 77 | // Look for a lexer rule in the tokenVocab file if it exists 78 | if ( getElement() instanceof LexerRuleRefNode ) { 79 | return TokenVocabResolver.resolveInTokenVocab(getElement(), ruleName); 80 | } 81 | 82 | return null; 83 | } 84 | 85 | @Override 86 | public PsiElement handleElementRename(String newElementName) throws IncorrectOperationException { 87 | Project project = getElement().getProject(); 88 | myElement.replace(MyPsiUtils.createLeafFromText(project, 89 | myElement.getContext(), 90 | newElementName, 91 | ANTLRv4TokenTypes.TOKEN_ELEMENT_TYPES.get(ANTLRv4Lexer.TOKEN_REF))); 92 | return myElement; 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /src/main/java/org/antlr/intellij/plugin/psi/GrammarElementRefNode.java: -------------------------------------------------------------------------------- 1 | package org.antlr.intellij.plugin.psi; 2 | 3 | import com.intellij.psi.impl.source.tree.LeafPsiElement; 4 | import com.intellij.psi.tree.IElementType; 5 | 6 | /** 7 | * Refs to tokens, rules 8 | */ 9 | public abstract class GrammarElementRefNode extends LeafPsiElement { 10 | public GrammarElementRefNode(IElementType type, CharSequence text) { 11 | super(type, text); 12 | } 13 | 14 | @Override 15 | public String getName() { 16 | return getText(); 17 | } 18 | 19 | @Override 20 | public String toString() { 21 | return getClass().getSimpleName() + "(" + getElementType() + ")"; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/org/antlr/intellij/plugin/psi/GrammarSpecNode.java: -------------------------------------------------------------------------------- 1 | package org.antlr.intellij.plugin.psi; 2 | 3 | import com.intellij.extapi.psi.ASTWrapperPsiElement; 4 | import com.intellij.lang.ASTNode; 5 | import com.intellij.psi.PsiElement; 6 | import org.antlr.intellij.adaptor.parser.PsiElementFactory; 7 | import org.jetbrains.annotations.NotNull; 8 | 9 | public class GrammarSpecNode extends ASTWrapperPsiElement { 10 | public GrammarSpecNode(@NotNull ASTNode node) { 11 | super(node); 12 | } 13 | 14 | public static class Factory implements PsiElementFactory { 15 | public static Factory INSTANCE = new Factory(); 16 | 17 | @Override 18 | public PsiElement createElement(ASTNode node) { 19 | return new GrammarSpecNode(node); 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /src/main/java/org/antlr/intellij/plugin/psi/LexerRuleRefNode.java: -------------------------------------------------------------------------------- 1 | package org.antlr.intellij.plugin.psi; 2 | 3 | import com.intellij.psi.PsiElement; 4 | import com.intellij.psi.PsiReference; 5 | import com.intellij.psi.tree.IElementType; 6 | 7 | public class LexerRuleRefNode extends GrammarElementRefNode { 8 | public LexerRuleRefNode(IElementType type, CharSequence text) { 9 | super(type, text); 10 | } 11 | 12 | @Override 13 | public PsiReference getReference() { 14 | if (isDeclaration()) { 15 | return null; 16 | } 17 | return new GrammarElementRef(this, getText()); 18 | } 19 | 20 | private boolean isDeclaration() { 21 | PsiElement parent = getParent(); 22 | return parent instanceof LexerRuleSpecNode 23 | || parent instanceof TokenSpecNode 24 | || parent instanceof ChannelSpecNode; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/org/antlr/intellij/plugin/psi/LexerRuleSpecNode.java: -------------------------------------------------------------------------------- 1 | package org.antlr.intellij.plugin.psi; 2 | 3 | import com.intellij.lang.ASTNode; 4 | import com.intellij.openapi.diagnostic.Logger; 5 | import com.intellij.psi.PsiElement; 6 | import com.intellij.psi.tree.IElementType; 7 | import com.intellij.psi.util.PsiTreeUtil; 8 | import org.antlr.intellij.adaptor.parser.PsiElementFactory; 9 | import org.antlr.intellij.plugin.ANTLRv4TokenTypes; 10 | import org.antlr.intellij.plugin.parser.ANTLRv4Lexer; 11 | import org.jetbrains.annotations.NotNull; 12 | 13 | public class LexerRuleSpecNode extends RuleSpecNode { 14 | public static final Logger LOG = Logger.getInstance("org.antlr.intellij.plugin.psi.LexerRuleSpecNode"); 15 | public LexerRuleSpecNode(@NotNull ASTNode node) { 16 | super(node); 17 | } 18 | 19 | @Override 20 | public IElementType getRuleRefType() { 21 | return ANTLRv4TokenTypes.TOKEN_ELEMENT_TYPES.get(ANTLRv4Lexer.TOKEN_REF); 22 | } 23 | 24 | @Override 25 | public GrammarElementRefNode getNameIdentifier() { 26 | GrammarElementRefNode tr = PsiTreeUtil.getChildOfType(this, LexerRuleRefNode.class); 27 | if ( tr==null ) { 28 | LOG.error("can't find LexerRuleRefNode child of "+this.getText(), (Throwable)null); 29 | } 30 | return tr; 31 | } 32 | 33 | public boolean isFragment() { 34 | return getNode().findChildByType(ANTLRv4TokenTypes.TOKEN_ELEMENT_TYPES.get(ANTLRv4Lexer.FRAGMENT)) != null; 35 | } 36 | 37 | public static class Factory implements PsiElementFactory { 38 | public static Factory INSTANCE = new Factory(); 39 | 40 | @Override 41 | public PsiElement createElement(ASTNode node) { 42 | return new LexerRuleSpecNode(node); 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/org/antlr/intellij/plugin/psi/ModeSpecNode.java: -------------------------------------------------------------------------------- 1 | package org.antlr.intellij.plugin.psi; 2 | 3 | import com.intellij.lang.ASTNode; 4 | import com.intellij.psi.PsiElement; 5 | import com.intellij.psi.tree.IElementType; 6 | import org.antlr.intellij.adaptor.parser.PsiElementFactory; 7 | import org.antlr.intellij.plugin.ANTLRv4TokenTypes; 8 | import org.antlr.intellij.plugin.parser.ANTLRv4Lexer; 9 | import org.antlr.intellij.plugin.parser.ANTLRv4Parser; 10 | import org.jetbrains.annotations.NotNull; 11 | 12 | import static org.antlr.intellij.plugin.psi.MyPsiUtils.findFirstChildOfType; 13 | 14 | /** 15 | * A node representing a lexical {@code mode} definition, and all its child rules. 16 | * 17 | * @implNote this is technically not a 'rule', but it has the same characteristics as a named rule so we 18 | * can extend {@code RuleSpecNode} 19 | */ 20 | public class ModeSpecNode extends RuleSpecNode { 21 | 22 | public ModeSpecNode(@NotNull ASTNode node) { 23 | super(node); 24 | } 25 | 26 | @Override 27 | public IElementType getRuleRefType() { 28 | return ANTLRv4TokenTypes.TOKEN_ELEMENT_TYPES.get(ANTLRv4Lexer.TOKEN_REF); 29 | } 30 | 31 | @Override 32 | public GrammarElementRefNode getNameIdentifier() { 33 | PsiElement idNode = findFirstChildOfType(this, ANTLRv4TokenTypes.getRuleElementType(ANTLRv4Parser.RULE_identifier)); 34 | 35 | if (idNode != null) { 36 | PsiElement firstChild = idNode.getFirstChild(); 37 | 38 | if (firstChild instanceof GrammarElementRefNode) { 39 | return (GrammarElementRefNode) firstChild; 40 | } 41 | } 42 | 43 | return null; 44 | } 45 | 46 | public static class Factory implements PsiElementFactory { 47 | public static Factory INSTANCE = new Factory(); 48 | 49 | @Override 50 | public PsiElement createElement(ASTNode node) { 51 | return new ModeSpecNode(node); 52 | } 53 | } 54 | } -------------------------------------------------------------------------------- /src/main/java/org/antlr/intellij/plugin/psi/ParserRuleRefNode.java: -------------------------------------------------------------------------------- 1 | package org.antlr.intellij.plugin.psi; 2 | 3 | import com.intellij.psi.PsiReference; 4 | import com.intellij.psi.tree.IElementType; 5 | 6 | public class ParserRuleRefNode extends GrammarElementRefNode { 7 | public ParserRuleRefNode(IElementType type, CharSequence text) { 8 | super(type, text); 9 | } 10 | 11 | @Override 12 | public PsiReference getReference() { 13 | if (isDeclaration()) { 14 | return null; 15 | } 16 | return new GrammarElementRef(this, getText()); 17 | } 18 | 19 | private boolean isDeclaration() { 20 | return getParent() instanceof ParserRuleSpecNode; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/org/antlr/intellij/plugin/psi/ParserRuleSpecNode.java: -------------------------------------------------------------------------------- 1 | package org.antlr.intellij.plugin.psi; 2 | 3 | import com.intellij.lang.ASTNode; 4 | import com.intellij.openapi.diagnostic.Logger; 5 | import com.intellij.psi.PsiElement; 6 | import com.intellij.psi.tree.IElementType; 7 | import com.intellij.psi.util.PsiTreeUtil; 8 | import org.antlr.intellij.adaptor.parser.PsiElementFactory; 9 | import org.antlr.intellij.plugin.ANTLRv4TokenTypes; 10 | import org.antlr.intellij.plugin.parser.ANTLRv4Lexer; 11 | import org.jetbrains.annotations.NotNull; 12 | 13 | public class ParserRuleSpecNode extends RuleSpecNode { 14 | public static final Logger LOG = Logger.getInstance("org.antlr.intellij.plugin.psi.ParserRuleSpecNode"); 15 | public ParserRuleSpecNode(@NotNull ASTNode node) { 16 | super(node); 17 | } 18 | 19 | @Override 20 | public IElementType getRuleRefType() { 21 | return ANTLRv4TokenTypes.TOKEN_ELEMENT_TYPES.get(ANTLRv4Lexer.RULE_REF); 22 | } 23 | 24 | @Override 25 | public GrammarElementRefNode getNameIdentifier() { 26 | GrammarElementRefNode rr = PsiTreeUtil.getChildOfType(this, ParserRuleRefNode.class); 27 | if ( rr==null ) { 28 | LOG.error("can't find ParserRuleRefNode child of "+this.getText(), (Throwable)null); 29 | } 30 | return rr; 31 | } 32 | 33 | public static class Factory implements PsiElementFactory { 34 | public static Factory INSTANCE = new Factory(); 35 | 36 | @Override 37 | public PsiElement createElement(ASTNode node) { 38 | return new ParserRuleSpecNode(node); 39 | } 40 | } 41 | } 42 | 43 | -------------------------------------------------------------------------------- /src/main/java/org/antlr/intellij/plugin/psi/RuleSpecNode.java: -------------------------------------------------------------------------------- 1 | package org.antlr.intellij.plugin.psi; 2 | 3 | import com.intellij.extapi.psi.ASTWrapperPsiElement; 4 | import com.intellij.lang.ASTNode; 5 | import com.intellij.psi.PsiElement; 6 | import com.intellij.psi.PsiNameIdentifierOwner; 7 | import com.intellij.psi.tree.IElementType; 8 | import com.intellij.util.IncorrectOperationException; 9 | import org.jetbrains.annotations.NonNls; 10 | import org.jetbrains.annotations.NotNull; 11 | import org.jetbrains.annotations.Nullable; 12 | 13 | /** Root of lexer, parser rule defs */ 14 | public abstract class RuleSpecNode extends ASTWrapperPsiElement implements PsiNameIdentifierOwner { 15 | protected String name = null; // an override to input text ID 16 | 17 | public RuleSpecNode(@NotNull final ASTNode node) { 18 | super(node); 19 | } 20 | 21 | @Override 22 | public String getName() { 23 | if ( name!=null ) return name; 24 | GrammarElementRefNode id = getNameIdentifier(); 25 | if ( id!=null ) { 26 | return id.getText(); 27 | } 28 | return "unknown-name"; 29 | } 30 | 31 | @Nullable 32 | public abstract GrammarElementRefNode getNameIdentifier(); 33 | 34 | @Override 35 | public PsiElement setName(@NonNls @NotNull String name) throws IncorrectOperationException { 36 | /* 37 | From doc: "Creating a fully correct AST node from scratch is 38 | quite difficult. Thus, surprisingly, the easiest way to 39 | get the replacement node is to create a dummy file in the 40 | custom language so that it would contain the necessary 41 | node in its parse tree, build the parse tree and 42 | extract the necessary node from it. 43 | */ 44 | GrammarElementRefNode id = getNameIdentifier(); 45 | if ( id != null ) { 46 | id.replace(MyPsiUtils.createLeafFromText(getProject(), 47 | getContext(), 48 | name, getRuleRefType())); 49 | } 50 | this.name = name; 51 | return this; 52 | } 53 | 54 | public abstract IElementType getRuleRefType(); 55 | 56 | @Override 57 | public void subtreeChanged() { 58 | super.subtreeChanged(); 59 | name = null; 60 | } 61 | 62 | @Override 63 | public int getTextOffset() { 64 | GrammarElementRefNode id = getNameIdentifier(); 65 | if ( id!=null ) return id.getTextOffset(); 66 | return super.getTextOffset(); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/main/java/org/antlr/intellij/plugin/psi/RulesNode.java: -------------------------------------------------------------------------------- 1 | package org.antlr.intellij.plugin.psi; 2 | 3 | import com.intellij.extapi.psi.ASTWrapperPsiElement; 4 | import com.intellij.lang.ASTNode; 5 | import com.intellij.psi.PsiElement; 6 | import org.antlr.intellij.adaptor.parser.PsiElementFactory; 7 | import org.jetbrains.annotations.NotNull; 8 | 9 | public class RulesNode extends ASTWrapperPsiElement { 10 | public RulesNode(@NotNull ASTNode node) { 11 | super(node); 12 | } 13 | 14 | public static class Factory implements PsiElementFactory { 15 | public static Factory INSTANCE = new Factory(); 16 | 17 | @Override 18 | public PsiElement createElement(ASTNode node) { 19 | return new RulesNode(node); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/org/antlr/intellij/plugin/psi/StringLiteralElement.java: -------------------------------------------------------------------------------- 1 | package org.antlr.intellij.plugin.psi; 2 | 3 | import com.intellij.psi.PsiElement; 4 | import com.intellij.psi.PsiReference; 5 | import com.intellij.psi.impl.source.tree.LeafPsiElement; 6 | import com.intellij.psi.tree.IElementType; 7 | 8 | import static org.antlr.intellij.plugin.ANTLRv4TokenTypes.RULE_ELEMENT_TYPES; 9 | import static org.antlr.intellij.plugin.parser.ANTLRv4Parser.RULE_optionValue; 10 | 11 | public class StringLiteralElement extends LeafPsiElement { 12 | public StringLiteralElement(IElementType type, CharSequence text) { 13 | super(type, text); 14 | } 15 | 16 | @Override 17 | public PsiReference getReference() { 18 | PsiElement parent = getParent(); 19 | 20 | if ( parent!=null && parent.getNode().getElementType()==RULE_ELEMENT_TYPES.get(RULE_optionValue) ) { 21 | return new StringLiteralRef(this); 22 | } 23 | 24 | return super.getReference(); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/org/antlr/intellij/plugin/psi/StringLiteralRef.java: -------------------------------------------------------------------------------- 1 | package org.antlr.intellij.plugin.psi; 2 | 3 | import com.intellij.openapi.util.TextRange; 4 | import com.intellij.psi.PsiElement; 5 | import com.intellij.psi.PsiReferenceBase; 6 | import com.intellij.util.ArrayUtilRt; 7 | import org.antlr.intellij.plugin.resolve.TokenVocabResolver; 8 | import org.jetbrains.annotations.Nullable; 9 | 10 | public class StringLiteralRef extends PsiReferenceBase { 11 | 12 | public StringLiteralRef(StringLiteralElement node) { 13 | super(node, TextRange.from(1, node.getTextLength() - 2)); 14 | } 15 | 16 | @Override // For compatibility with 2017.x 17 | public Object[] getVariants() { 18 | return ArrayUtilRt.EMPTY_OBJECT_ARRAY; 19 | } 20 | 21 | @Override 22 | public @Nullable PsiElement resolve() { 23 | return TokenVocabResolver.resolveTokenVocabFile(myElement); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/org/antlr/intellij/plugin/psi/TokenSpecNode.java: -------------------------------------------------------------------------------- 1 | package org.antlr.intellij.plugin.psi; 2 | 3 | import com.intellij.extapi.psi.ASTWrapperPsiElement; 4 | import com.intellij.lang.ASTNode; 5 | import com.intellij.psi.PsiElement; 6 | import com.intellij.psi.tree.IElementType; 7 | import com.intellij.psi.util.PsiTreeUtil; 8 | import org.antlr.intellij.adaptor.parser.PsiElementFactory; 9 | import org.antlr.intellij.plugin.ANTLRv4TokenTypes; 10 | import org.antlr.intellij.plugin.parser.ANTLRv4Lexer; 11 | import org.antlr.intellij.plugin.parser.ANTLRv4Parser; 12 | import org.jetbrains.annotations.NotNull; 13 | 14 | /** 15 | * A token defined in the {@code tokens} section. 16 | */ 17 | public class TokenSpecNode extends RuleSpecNode { 18 | 19 | public TokenSpecNode(@NotNull ASTNode node) { 20 | super(node); 21 | } 22 | 23 | @Override 24 | public GrammarElementRefNode getNameIdentifier() { 25 | return PsiTreeUtil.getChildOfType(this, LexerRuleRefNode.class); 26 | } 27 | 28 | @Override 29 | public IElementType getRuleRefType() { 30 | return ANTLRv4TokenTypes.TOKEN_ELEMENT_TYPES.get(ANTLRv4Lexer.TOKEN_REF); 31 | } 32 | 33 | public static class Factory implements PsiElementFactory { 34 | public static Factory INSTANCE = new Factory(); 35 | 36 | @Override 37 | public PsiElement createElement(ASTNode node) { 38 | ASTNode idList = node.getTreeParent(); 39 | ASTNode parent = null; 40 | 41 | if (idList != null) { 42 | parent = idList.getTreeParent(); 43 | } 44 | if (parent != null) { 45 | if (parent.getElementType() == ANTLRv4TokenTypes.RULE_ELEMENT_TYPES.get(ANTLRv4Parser.RULE_tokensSpec)) { 46 | return new TokenSpecNode(node); 47 | } 48 | else if (parent.getElementType() == ANTLRv4TokenTypes.RULE_ELEMENT_TYPES.get(ANTLRv4Parser.RULE_channelsSpec)) { 49 | return new ChannelSpecNode(node); 50 | } 51 | } 52 | 53 | return new ASTWrapperPsiElement(node); 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/org/antlr/intellij/plugin/refactor/ANTLRv4RefactoringSupport.java: -------------------------------------------------------------------------------- 1 | package org.antlr.intellij.plugin.refactor; 2 | 3 | import com.intellij.lang.refactoring.RefactoringSupportProvider; 4 | import com.intellij.psi.PsiElement; 5 | import org.antlr.intellij.plugin.ANTLRv4Language; 6 | import org.antlr.intellij.plugin.psi.RuleSpecNode; 7 | import org.jetbrains.annotations.NotNull; 8 | import org.jetbrains.annotations.Nullable; 9 | 10 | public class ANTLRv4RefactoringSupport extends RefactoringSupportProvider{ 11 | 12 | public boolean isAvailable(@NotNull PsiElement context){ 13 | return context.getLanguage().isKindOf(ANTLRv4Language.INSTANCE); 14 | } 15 | 16 | // variable in-place rename only applies to elements limited to one file 17 | public boolean isMemberInplaceRenameAvailable(@NotNull PsiElement element, @Nullable PsiElement context){ 18 | return element instanceof RuleSpecNode; 19 | } 20 | } -------------------------------------------------------------------------------- /src/main/java/org/antlr/intellij/plugin/resolve/ImportResolver.java: -------------------------------------------------------------------------------- 1 | package org.antlr.intellij.plugin.resolve; 2 | 3 | import com.intellij.lang.ASTNode; 4 | import com.intellij.psi.PsiElement; 5 | import com.intellij.psi.PsiFile; 6 | import com.intellij.psi.PsiRecursiveElementVisitor; 7 | import com.intellij.psi.util.PsiTreeUtil; 8 | import org.antlr.intellij.plugin.parser.ANTLRv4Parser; 9 | import org.antlr.intellij.plugin.psi.GrammarElementRefNode; 10 | import org.antlr.intellij.plugin.psi.GrammarSpecNode; 11 | import org.antlr.intellij.plugin.psi.MyPsiUtils; 12 | import org.jetbrains.annotations.NotNull; 13 | 14 | import java.util.ArrayList; 15 | import java.util.List; 16 | 17 | import static org.antlr.intellij.plugin.ANTLRv4TokenTypes.RULE_ELEMENT_TYPES; 18 | import static org.antlr.intellij.plugin.resolve.TokenVocabResolver.findRelativeFile; 19 | 20 | public class ImportResolver { 21 | 22 | public static PsiFile resolveImportedFile(GrammarElementRefNode reference) { 23 | PsiElement importStatement = PsiTreeUtil.findFirstParent(reference, ImportResolver::isImportStatement); 24 | 25 | if ( importStatement!=null ) { 26 | return findRelativeFile(reference.getText(), reference.getContainingFile()); 27 | } 28 | 29 | return null; 30 | } 31 | 32 | private static boolean isImportStatement(PsiElement el) { 33 | ASTNode node = el.getNode(); 34 | return node != null && node.getElementType() == RULE_ELEMENT_TYPES.get(ANTLRv4Parser.RULE_delegateGrammar); 35 | } 36 | 37 | public static PsiElement resolveInImportedFiles(@NotNull PsiFile grammarFile, @NotNull String ruleName) { 38 | return resolveInImportedFiles(grammarFile, ruleName, new ArrayList<>()); 39 | } 40 | 41 | private static PsiElement resolveInImportedFiles(PsiFile grammarFile, String ruleName, List visitedFiles) { 42 | DelegateGrammarsVisitor visitor = new DelegateGrammarsVisitor(); 43 | grammarFile.accept(visitor); 44 | 45 | for ( PsiFile importedGrammar : visitor.importedGrammars ) { 46 | if ( visitedFiles.contains(importedGrammar) ) { 47 | continue; 48 | } 49 | visitedFiles.add(importedGrammar); 50 | 51 | GrammarSpecNode grammar = PsiTreeUtil.getChildOfType(importedGrammar, GrammarSpecNode.class); 52 | PsiElement specNode = MyPsiUtils.findSpecNode(grammar, ruleName); 53 | 54 | if ( specNode!=null ) { 55 | return specNode; 56 | } 57 | 58 | // maybe the imported grammar also imports other grammars itself? 59 | specNode = resolveInImportedFiles(importedGrammar, ruleName, visitedFiles); 60 | if ( specNode!=null ) { 61 | return specNode; 62 | } 63 | } 64 | 65 | return null; 66 | } 67 | 68 | private static class DelegateGrammarsVisitor extends PsiRecursiveElementVisitor { 69 | 70 | List importedGrammars = new ArrayList<>(); 71 | 72 | @Override 73 | public void visitElement(PsiElement element) { 74 | if ( isImportStatement(element) ) { 75 | PsiFile importedGrammar = findRelativeFile(element.getText(), element.getContainingFile()); 76 | 77 | if (importedGrammar != null) { 78 | importedGrammars.add(importedGrammar); 79 | } 80 | } 81 | super.visitElement(element); 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/main/java/org/antlr/intellij/plugin/resolve/TokenVocabResolver.java: -------------------------------------------------------------------------------- 1 | package org.antlr.intellij.plugin.resolve; 2 | 3 | import com.intellij.lang.ASTNode; 4 | import com.intellij.psi.PsiDirectory; 5 | import com.intellij.psi.PsiElement; 6 | import com.intellij.psi.PsiFile; 7 | import com.intellij.psi.util.PsiTreeUtil; 8 | import org.antlr.intellij.plugin.ANTLRv4FileRoot; 9 | import org.antlr.intellij.plugin.parser.ANTLRv4Parser; 10 | import org.antlr.intellij.plugin.psi.*; 11 | import org.apache.commons.lang.StringUtils; 12 | import org.jetbrains.annotations.Nullable; 13 | 14 | import static org.antlr.intellij.plugin.ANTLRv4TokenTypes.RULE_ELEMENT_TYPES; 15 | 16 | public class TokenVocabResolver { 17 | 18 | /** 19 | * If this reference is the value of a {@code tokenVocab} option, returns the corresponding 20 | * grammar file. 21 | */ 22 | @Nullable 23 | public static PsiFile resolveTokenVocabFile(PsiElement reference) { 24 | PsiElement optionValue = PsiTreeUtil.findFirstParent(reference, TokenVocabResolver::isOptionValue); 25 | 26 | if (optionValue != null) { 27 | PsiElement option = optionValue.getParent(); 28 | 29 | if (option != null) { 30 | PsiElement optionName = PsiTreeUtil.getDeepestFirst(option); 31 | 32 | if (optionName.getText().equals("tokenVocab")) { 33 | String text = StringUtils.strip(reference.getText(), "'"); 34 | return findRelativeFile(text, reference.getContainingFile()); 35 | } 36 | } 37 | } 38 | 39 | return null; 40 | } 41 | 42 | /** 43 | * Tries to find a declaration named {@code ruleName} in the {@code tokenVocab} file if it exists. 44 | */ 45 | @Nullable 46 | public static PsiElement resolveInTokenVocab(GrammarElementRefNode reference, String ruleName) { 47 | String tokenVocab = MyPsiUtils.findTokenVocabIfAny((ANTLRv4FileRoot) reference.getContainingFile()); 48 | 49 | if (tokenVocab != null) { 50 | PsiFile tokenVocabFile = findRelativeFile(tokenVocab, reference.getContainingFile()); 51 | 52 | if (tokenVocabFile != null) { 53 | GrammarSpecNode lexerGrammar = PsiTreeUtil.findChildOfType(tokenVocabFile, GrammarSpecNode.class); 54 | PsiElement node = MyPsiUtils.findSpecNode(lexerGrammar, ruleName); 55 | 56 | if (node instanceof LexerRuleSpecNode) { 57 | // fragments are not visible to the parser 58 | if (!((LexerRuleSpecNode) node).isFragment()) { 59 | return node; 60 | } 61 | } 62 | if (node instanceof TokenSpecNode) { 63 | return node; 64 | } 65 | } 66 | } 67 | 68 | return null; 69 | } 70 | 71 | private static boolean isOptionValue(PsiElement el) { 72 | ASTNode node = el.getNode(); 73 | return node != null && node.getElementType() == RULE_ELEMENT_TYPES.get(ANTLRv4Parser.RULE_optionValue); 74 | } 75 | 76 | /** 77 | * Looks for an ANTLR grammar file named {@code }.g4 next to the given {@code sibling} file. 78 | */ 79 | static PsiFile findRelativeFile(String baseName, PsiFile sibling) { 80 | PsiDirectory parentDirectory = sibling.getParent(); 81 | 82 | if (parentDirectory != null) { 83 | PsiFile candidate = parentDirectory.findFile(baseName + ".g4"); 84 | 85 | if (candidate instanceof ANTLRv4FileRoot) { 86 | return candidate; 87 | } 88 | } 89 | 90 | return null; 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /src/main/java/org/antlr/intellij/plugin/structview/ANTLRv4ItemPresentation.java: -------------------------------------------------------------------------------- 1 | package org.antlr.intellij.plugin.structview; 2 | 3 | import com.intellij.lang.ASTNode; 4 | import com.intellij.navigation.ItemPresentation; 5 | import com.intellij.psi.PsiElement; 6 | import com.intellij.psi.util.PsiTreeUtil; 7 | import org.antlr.intellij.plugin.ANTLRv4FileRoot; 8 | import org.antlr.intellij.plugin.ANTLRv4TokenTypes; 9 | import org.antlr.intellij.plugin.parser.ANTLRv4Parser; 10 | import org.antlr.intellij.plugin.psi.GrammarElementRefNode; 11 | import org.antlr.intellij.plugin.psi.GrammarSpecNode; 12 | import org.antlr.intellij.plugin.psi.ModeSpecNode; 13 | import org.antlr.intellij.plugin.psi.MyPsiUtils; 14 | import org.jetbrains.annotations.Nullable; 15 | 16 | import javax.swing.*; 17 | 18 | public class ANTLRv4ItemPresentation implements ItemPresentation { 19 | protected final PsiElement element; 20 | 21 | protected ANTLRv4ItemPresentation(PsiElement element) { 22 | this.element = element; 23 | } 24 | 25 | @Nullable 26 | public String getLocationString() { 27 | return null; 28 | } 29 | 30 | @Override 31 | public String getPresentableText() { 32 | if (element instanceof ANTLRv4FileRoot) { 33 | GrammarSpecNode gnode = PsiTreeUtil.findChildOfType(element, GrammarSpecNode.class); 34 | PsiElement id = MyPsiUtils.findChildOfType(gnode, ANTLRv4TokenTypes.RULE_ELEMENT_TYPES.get(ANTLRv4Parser.RULE_identifier)); 35 | if ( id!=null ) { 36 | return id.getText(); 37 | } 38 | return ""; 39 | } 40 | if ( element instanceof ModeSpecNode ) { 41 | ModeSpecNode mode = (ModeSpecNode) element; 42 | GrammarElementRefNode modeId = mode.getNameIdentifier(); 43 | if ( modeId!=null ) { 44 | return modeId.getName(); 45 | } 46 | return ""; 47 | } 48 | ASTNode node = element.getNode(); 49 | return node.getText(); 50 | } 51 | 52 | @Nullable 53 | public Icon getIcon(boolean open) { 54 | return element.getIcon(0); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/org/antlr/intellij/plugin/structview/ANTLRv4StructureViewElement.java: -------------------------------------------------------------------------------- 1 | package org.antlr.intellij.plugin.structview; 2 | 3 | import com.intellij.ide.structureView.StructureViewTreeElement; 4 | import com.intellij.ide.util.treeView.smartTree.TreeElement; 5 | import com.intellij.navigation.ItemPresentation; 6 | import com.intellij.navigation.NavigationItem; 7 | import com.intellij.psi.PsiElement; 8 | import com.intellij.psi.PsiRecursiveElementVisitor; 9 | import com.intellij.psi.util.PsiTreeUtil; 10 | import org.antlr.intellij.plugin.ANTLRv4FileRoot; 11 | import org.antlr.intellij.plugin.psi.*; 12 | import org.jetbrains.annotations.NotNull; 13 | 14 | import java.util.ArrayList; 15 | import java.util.List; 16 | import java.util.Objects; 17 | 18 | public class ANTLRv4StructureViewElement implements StructureViewTreeElement { 19 | private final PsiElement element; 20 | 21 | public ANTLRv4StructureViewElement(PsiElement element) { 22 | this.element = element; 23 | } 24 | 25 | @Override 26 | public Object getValue() { 27 | return element; 28 | } 29 | 30 | @Override 31 | public void navigate(boolean requestFocus) { 32 | if (element instanceof NavigationItem) { 33 | ((NavigationItem) element).navigate(requestFocus); 34 | } 35 | } 36 | 37 | @Override 38 | public boolean canNavigate() { 39 | return element instanceof NavigationItem && 40 | ((NavigationItem)element).canNavigate(); 41 | } 42 | 43 | @Override 44 | public boolean canNavigateToSource() { 45 | return element instanceof NavigationItem && 46 | ((NavigationItem)element).canNavigateToSource(); 47 | } 48 | 49 | @NotNull 50 | @Override 51 | public ItemPresentation getPresentation() { 52 | return new ANTLRv4ItemPresentation(element); 53 | } 54 | 55 | @NotNull 56 | @Override 57 | public TreeElement[] getChildren() { 58 | List treeElements = new ArrayList<>(); 59 | 60 | if (element instanceof ANTLRv4FileRoot) { 61 | new PsiRecursiveElementVisitor() { 62 | @Override 63 | public void visitElement(PsiElement element) { 64 | if ( element instanceof ModeSpecNode ) { 65 | treeElements.add(new ANTLRv4StructureViewElement(element)); 66 | return; 67 | } 68 | 69 | if ( element instanceof LexerRuleSpecNode || element instanceof ParserRuleSpecNode ) { 70 | PsiElement rule = PsiTreeUtil.findChildOfAnyType(element, LexerRuleRefNode.class, ParserRuleRefNode.class); 71 | if (rule != null) { 72 | treeElements.add(new ANTLRv4StructureViewElement(rule)); 73 | } 74 | } 75 | 76 | super.visitElement(element); 77 | } 78 | }.visitElement(element); 79 | } 80 | else if ( element instanceof ModeSpecNode ) { 81 | LexerRuleSpecNode[] lexerRules = PsiTreeUtil.getChildrenOfType(element, LexerRuleSpecNode.class); 82 | 83 | if ( lexerRules != null ) { 84 | for ( LexerRuleSpecNode lexerRule : lexerRules ) { 85 | treeElements.add(new ANTLRv4StructureViewElement(PsiTreeUtil.findChildOfType(lexerRule, LexerRuleRefNode.class))); 86 | } 87 | } 88 | } 89 | 90 | return treeElements.toArray(new TreeElement[0]); 91 | } 92 | 93 | // probably not critical 94 | @Override 95 | public boolean equals(Object o) { 96 | if (this == o) return true; 97 | if (o == null || getClass() != o.getClass()) return false; 98 | 99 | ANTLRv4StructureViewElement that = (ANTLRv4StructureViewElement)o; 100 | 101 | return Objects.equals(element, that.element); 102 | } 103 | 104 | @Override 105 | public int hashCode() { 106 | return element != null ? element.hashCode() : 0; 107 | } 108 | 109 | } 110 | -------------------------------------------------------------------------------- /src/main/java/org/antlr/intellij/plugin/structview/ANTLRv4StructureViewFactory.java: -------------------------------------------------------------------------------- 1 | package org.antlr.intellij.plugin.structview; 2 | 3 | import com.intellij.ide.structureView.StructureViewBuilder; 4 | import com.intellij.ide.structureView.StructureViewModel; 5 | import com.intellij.ide.structureView.StructureViewModelBase; 6 | import com.intellij.ide.structureView.StructureViewTreeElement; 7 | import com.intellij.ide.structureView.TreeBasedStructureViewBuilder; 8 | import com.intellij.ide.structureView.impl.common.PsiTreeElementBase; 9 | import com.intellij.lang.PsiStructureViewFactory; 10 | import com.intellij.openapi.editor.Editor; 11 | import com.intellij.openapi.vfs.VirtualFile; 12 | import com.intellij.psi.PsiElement; 13 | import com.intellij.psi.PsiFile; 14 | import org.antlr.intellij.plugin.ANTLRv4FileRoot; 15 | import org.jetbrains.annotations.NotNull; 16 | import org.jetbrains.annotations.Nullable; 17 | 18 | import java.util.Collection; 19 | import java.util.Collections; 20 | 21 | public class ANTLRv4StructureViewFactory implements PsiStructureViewFactory { 22 | /** fake a blank Treeview with a warning */ 23 | public static class DummyViewTreeElement extends PsiTreeElementBase { 24 | public DummyViewTreeElement(PsiElement psiElement) { 25 | super(psiElement); 26 | } 27 | 28 | @NotNull 29 | @Override 30 | public Collection getChildrenBase() { 31 | return Collections.emptyList(); 32 | } 33 | 34 | @Nullable 35 | @Override 36 | public String getPresentableText() { 37 | return "Sorry .g not supported (use .g4)"; 38 | } 39 | } 40 | 41 | @Nullable 42 | @Override 43 | public StructureViewBuilder getStructureViewBuilder(final PsiFile psiFile) { 44 | return new TreeBasedStructureViewBuilder() { 45 | @NotNull 46 | @Override 47 | public StructureViewModel createStructureViewModel(@Nullable Editor editor) { 48 | VirtualFile grammarFile = psiFile.getVirtualFile(); 49 | if ( grammarFile==null || !grammarFile.getName().endsWith(".g4") ) { 50 | return new StructureViewModelBase(psiFile, new DummyViewTreeElement(psiFile)); 51 | } 52 | return new ANTLRv4StructureViewModel((ANTLRv4FileRoot)psiFile); 53 | } 54 | }; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/org/antlr/intellij/plugin/structview/ANTLRv4StructureViewModel.java: -------------------------------------------------------------------------------- 1 | package org.antlr.intellij.plugin.structview; 2 | 3 | import com.intellij.icons.AllIcons; 4 | import com.intellij.ide.structureView.StructureViewModel; 5 | import com.intellij.ide.structureView.StructureViewModelBase; 6 | import com.intellij.ide.structureView.StructureViewTreeElement; 7 | import com.intellij.ide.util.treeView.smartTree.ActionPresentation; 8 | import com.intellij.ide.util.treeView.smartTree.ActionPresentationData; 9 | import com.intellij.ide.util.treeView.smartTree.Sorter; 10 | import com.intellij.ide.util.treeView.smartTree.SorterUtil; 11 | import com.intellij.psi.PsiFile; 12 | import org.antlr.intellij.plugin.ANTLRv4FileRoot; 13 | import org.antlr.intellij.plugin.psi.LexerRuleSpecNode; 14 | import org.antlr.intellij.plugin.psi.ParserRuleSpecNode; 15 | import org.jetbrains.annotations.NotNull; 16 | 17 | import java.util.Comparator; 18 | 19 | public class ANTLRv4StructureViewModel 20 | extends StructureViewModelBase 21 | // extends TextEditorBasedStructureViewModel 22 | implements StructureViewModel.ElementInfoProvider 23 | { 24 | private static final Sorter PARSER_LEXER_RULE_SORTER = new Sorter() { 25 | public Comparator getComparator() { 26 | return (o1, o2) -> { 27 | String s1 = SorterUtil.getStringPresentation(o1); 28 | String s2 = SorterUtil.getStringPresentation(o2); 29 | // flip case of char 0 so it puts parser rules first 30 | if ( Character.isLowerCase(s1.charAt(0)) ) { 31 | s1 = Character.toUpperCase(s1.charAt(0))+s1.substring(1); 32 | } 33 | else { 34 | s1 = Character.toLowerCase(s1.charAt(0))+s1.substring(1); 35 | } 36 | if ( Character.isLowerCase(s2.charAt(0)) ) { 37 | s2 = Character.toUpperCase(s2.charAt(0))+s2.substring(1); 38 | } 39 | else { 40 | s2 = Character.toLowerCase(s2.charAt(0))+s2.substring(1); 41 | } 42 | return s1.compareTo(s2); 43 | }; 44 | } 45 | 46 | public boolean isVisible() { 47 | return true; 48 | } 49 | 50 | @NotNull 51 | public ActionPresentation getPresentation() { 52 | // how it's described in sort by dropdown in nav window. 53 | String name = "Sort by rule type"; 54 | return new ActionPresentationData(name, name, AllIcons.ObjectBrowser.SortByType); 55 | } 56 | 57 | @NotNull 58 | public String getName() { 59 | return "PARSER_LEXER_RULE_SORTER"; 60 | } 61 | }; 62 | 63 | ANTLRv4FileRoot rootElement; 64 | 65 | public ANTLRv4StructureViewModel(ANTLRv4FileRoot rootElement) { 66 | super(rootElement, new ANTLRv4StructureViewElement(rootElement)); 67 | this.rootElement = rootElement; 68 | } 69 | 70 | 71 | @NotNull 72 | public Sorter[] getSorters() { 73 | return new Sorter[] {PARSER_LEXER_RULE_SORTER, Sorter.ALPHA_SORTER}; 74 | } 75 | 76 | @Override 77 | protected PsiFile getPsiFile() { 78 | return rootElement; 79 | } 80 | 81 | @Override 82 | public boolean isAlwaysShowsPlus(StructureViewTreeElement element) { 83 | Object value = element.getValue(); 84 | return value instanceof ANTLRv4FileRoot; 85 | } 86 | 87 | @Override 88 | public boolean isAlwaysLeaf(StructureViewTreeElement element) { 89 | Object value = element.getValue(); 90 | return value instanceof ParserRuleSpecNode || value instanceof LexerRuleSpecNode; 91 | } 92 | 93 | /** 94 | Intellij: The implementation of StructureViewTreeElement.getChildren() 95 | needs to be matched by TextEditorBasedStructureViewModel.getSuitableClasses(). 96 | The latter method returns an array of PsiElement-derived classes which can 97 | be shown as structure view elements, and is used to select the Structure 98 | View item matching the cursor position when the structure view is first 99 | opened or when the "Autoscroll from source" option is used. 100 | */ 101 | @NotNull 102 | protected Class[] getSuitableClasses() { 103 | return new Class[] {ANTLRv4FileRoot.class, 104 | LexerRuleSpecNode.class, 105 | ParserRuleSpecNode.class}; 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /src/main/java/org/antlr/intellij/plugin/templates/ANTLRGenericContext.java: -------------------------------------------------------------------------------- 1 | package org.antlr.intellij.plugin.templates; 2 | 3 | import com.intellij.codeInsight.template.EverywhereContextType; 4 | import com.intellij.psi.PsiElement; 5 | import com.intellij.psi.PsiFile; 6 | import org.jetbrains.annotations.NotNull; 7 | 8 | public class ANTLRGenericContext extends ANTLRLiveTemplateContext { 9 | public ANTLRGenericContext() { 10 | super("ANTLR", "ANTLR", EverywhereContextType.class); 11 | } 12 | 13 | @Override 14 | protected boolean isInContext(@NotNull PsiFile file, @NotNull PsiElement element, int offset) { 15 | return false; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/org/antlr/intellij/plugin/templates/ANTLRLiveTemplateContext.java: -------------------------------------------------------------------------------- 1 | package org.antlr.intellij.plugin.templates; 2 | 3 | import com.intellij.codeInsight.template.TemplateContextType; 4 | import com.intellij.psi.PsiElement; 5 | import com.intellij.psi.PsiFile; 6 | import com.intellij.psi.util.PsiUtilBase; 7 | import org.antlr.intellij.plugin.ANTLRv4Language; 8 | import org.jetbrains.annotations.NonNls; 9 | import org.jetbrains.annotations.NotNull; 10 | import org.jetbrains.annotations.Nullable; 11 | 12 | public abstract class ANTLRLiveTemplateContext extends TemplateContextType { 13 | public ANTLRLiveTemplateContext(@NotNull @NonNls String id, 14 | @NotNull String presentableName, 15 | @Nullable Class baseContextType) 16 | { 17 | super(id, presentableName, baseContextType); 18 | } 19 | 20 | protected abstract boolean isInContext(@NotNull PsiFile file, @NotNull PsiElement element, int offset); 21 | 22 | @Override 23 | public boolean isInContext(@NotNull PsiFile file, int offset) { 24 | // offset is where cursor or insertion point is I guess 25 | if ( !PsiUtilBase.getLanguageAtOffset(file, offset).isKindOf(ANTLRv4Language.INSTANCE) ) { 26 | return false; 27 | } 28 | if ( offset==file.getTextLength() ) { // allow at EOF 29 | offset--; 30 | } 31 | PsiElement element = file.findElementAt(offset); 32 | 33 | if ( element==null ) { 34 | return false; 35 | } 36 | 37 | return isInContext(file, element, offset); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/org/antlr/intellij/plugin/templates/ANTLRLiveTemplatesProvider.java: -------------------------------------------------------------------------------- 1 | package org.antlr.intellij.plugin.templates; 2 | 3 | import com.intellij.codeInsight.template.impl.DefaultLiveTemplatesProvider; 4 | import org.jetbrains.annotations.Nullable; 5 | 6 | public class ANTLRLiveTemplatesProvider implements DefaultLiveTemplatesProvider { 7 | // make sure module shows liveTemplates as source dir or whatever dir holds "lexer" 8 | public static final String[] TEMPLATES = {"liveTemplates/lexer/user"}; 9 | 10 | @Override 11 | public String[] getDefaultLiveTemplateFiles() { 12 | return TEMPLATES; 13 | } 14 | 15 | @Nullable 16 | @Override 17 | public String[] getHiddenLiveTemplateFiles() { 18 | return new String[0]; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/org/antlr/intellij/plugin/templates/OutsideRuleContext.java: -------------------------------------------------------------------------------- 1 | package org.antlr.intellij.plugin.templates; 2 | 3 | import com.intellij.psi.PsiElement; 4 | import com.intellij.psi.PsiFile; 5 | import org.antlr.intellij.plugin.parser.ANTLRv4Parser; 6 | import org.antlr.intellij.plugin.parsing.ParsingUtils; 7 | import org.antlr.v4.runtime.CommonTokenStream; 8 | import org.antlr.v4.runtime.Token; 9 | import org.jetbrains.annotations.NotNull; 10 | 11 | public class OutsideRuleContext extends ANTLRLiveTemplateContext { 12 | public OutsideRuleContext() { 13 | super("ANTLR_OUTSIDE", "Outside rule", ANTLRGenericContext.class); 14 | } 15 | 16 | @Override 17 | public boolean isInContext(@NotNull PsiFile file, PsiElement element, int offset) { 18 | CommonTokenStream tokens = ParsingUtils.tokenizeANTLRGrammar(file.getText()); 19 | Token tokenUnderCursor = ParsingUtils.getTokenUnderCursor(tokens, offset); 20 | if ( tokenUnderCursor==null ) { 21 | return false; // sometimes happens at the eof 22 | } 23 | int tokenIndex = tokenUnderCursor.getTokenIndex(); 24 | Token nextRealToken = ParsingUtils.nextRealToken(tokens, tokenIndex); 25 | Token previousRealToken = ParsingUtils.previousRealToken(tokens, tokenIndex); 26 | 27 | if ( nextRealToken==null || previousRealToken==null ) { 28 | return false; 29 | } 30 | 31 | int previousRealTokenType = previousRealToken.getType(); 32 | int nextRealTokenType = nextRealToken.getType(); 33 | 34 | if ( previousRealTokenType== ANTLRv4Parser.BEGIN_ACTION ) { 35 | // make sure we're not in a rule; has to be @lexer::header {...} stuff 36 | Token prevPrevRealToken = ParsingUtils.previousRealToken(tokens, previousRealToken.getTokenIndex()); 37 | if ( prevPrevRealToken==null ) { 38 | return false; 39 | } 40 | Token prevPrevPrevRealToken = ParsingUtils.previousRealToken(tokens, prevPrevRealToken.getTokenIndex()); 41 | if ( prevPrevPrevRealToken==null ) { 42 | return false; 43 | } 44 | if ( prevPrevPrevRealToken.getType()!=ANTLRv4Parser.AT && 45 | prevPrevPrevRealToken.getType()!=ANTLRv4Parser.COLONCOLON ) 46 | { 47 | return false; 48 | } 49 | } 50 | 51 | boolean okBefore = 52 | previousRealTokenType == ANTLRv4Parser.RBRACE || 53 | previousRealTokenType == ANTLRv4Parser.SEMI || 54 | previousRealTokenType == ANTLRv4Parser.BEGIN_ACTION; 55 | boolean okAfter = 56 | nextRealTokenType == ANTLRv4Parser.TOKEN_REF || 57 | nextRealTokenType == ANTLRv4Parser.RULE_REF || 58 | nextRealTokenType == Token.EOF; 59 | 60 | return okBefore && okAfter; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/main/java/org/antlr/intellij/plugin/validation/CreateRuleFix.java: -------------------------------------------------------------------------------- 1 | package org.antlr.intellij.plugin.validation; 2 | 3 | import com.intellij.codeInsight.intention.impl.BaseIntentionAction; 4 | import com.intellij.codeInsight.template.Template; 5 | import com.intellij.codeInsight.template.TemplateManager; 6 | import com.intellij.codeInsight.template.impl.TextExpression; 7 | import com.intellij.openapi.editor.Editor; 8 | import com.intellij.openapi.project.Project; 9 | import com.intellij.openapi.util.TextRange; 10 | import com.intellij.psi.PsiDocumentManager; 11 | import com.intellij.psi.PsiElement; 12 | import com.intellij.psi.PsiFile; 13 | import com.intellij.psi.util.PsiTreeUtil; 14 | import com.intellij.util.IncorrectOperationException; 15 | import org.antlr.intellij.plugin.psi.MyPsiUtils; 16 | import org.antlr.intellij.plugin.psi.RuleSpecNode; 17 | import org.jetbrains.annotations.Nls; 18 | import org.jetbrains.annotations.NotNull; 19 | 20 | import static org.antlr.intellij.plugin.ANTLRv4TokenTypes.getTokenElementType; 21 | import static org.antlr.intellij.plugin.parser.ANTLRv4Lexer.SEMI; 22 | 23 | /** 24 | * A quick fix to create missing rules. 25 | */ 26 | public class CreateRuleFix extends BaseIntentionAction { 27 | 28 | private final TextRange textRange; 29 | private final String ruleName; 30 | 31 | public CreateRuleFix(TextRange textRange, PsiFile file) { 32 | this.textRange = textRange; 33 | ruleName = textRange.substring(file.getText()); 34 | } 35 | 36 | @Nls(capitalization = Nls.Capitalization.Sentence) 37 | @NotNull 38 | @Override 39 | public String getFamilyName() { 40 | return "ANTLR4"; 41 | } 42 | 43 | @Nls(capitalization = Nls.Capitalization.Sentence) 44 | @NotNull 45 | @Override 46 | public String getText() { 47 | return "Create rule '" + ruleName + "'"; 48 | } 49 | 50 | @Override 51 | public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) { 52 | return true; 53 | } 54 | 55 | @Override 56 | public void invoke(@NotNull Project project, Editor editor, PsiFile file) throws IncorrectOperationException { 57 | String ruleName = editor.getDocument().getText(textRange); 58 | 59 | prepareEditor(project, editor, file); 60 | 61 | Template template = TemplateManager.getInstance(project).createTemplate("", ""); 62 | template.addTextSegment(ruleName + ": "); 63 | template.addVariable("CONTENT", new TextExpression("' '"), true); 64 | template.addTextSegment(";"); 65 | 66 | TemplateManager.getInstance(project).startTemplate(editor, template); 67 | } 68 | 69 | private void prepareEditor(@NotNull Project project, Editor editor, PsiFile file) { 70 | int insertionPoint = findInsertionPoint(editor, file); 71 | editor.getDocument().insertString(insertionPoint, "\n\n"); 72 | 73 | PsiDocumentManager.getInstance(project).commitDocument(editor.getDocument()); 74 | 75 | editor.getCaretModel().moveToOffset(insertionPoint + 2); 76 | } 77 | 78 | private int findInsertionPoint(Editor editor, PsiFile file) { 79 | PsiElement atRange = file.findElementAt(textRange.getEndOffset()); 80 | if ( atRange!=null ) { 81 | RuleSpecNode parentRule = PsiTreeUtil.getParentOfType(atRange, RuleSpecNode.class); 82 | 83 | if ( parentRule!=null ) { 84 | PsiElement semi = MyPsiUtils.findFirstChildOfType(parentRule, getTokenElementType(SEMI)); 85 | 86 | if ( semi!=null ) { 87 | return semi.getTextOffset() + 1; 88 | } 89 | return parentRule.getTextRange().getEndOffset(); 90 | } 91 | } 92 | 93 | return editor.getDocument().getLineEndOffset(editor.getDocument().getLineCount() - 1); 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /src/main/java/org/antlr/intellij/plugin/validation/GrammarInfoMessage.java: -------------------------------------------------------------------------------- 1 | package org.antlr.intellij.plugin.validation; 2 | 3 | import org.antlr.runtime.Token; 4 | import org.antlr.v4.tool.GrammarSemanticsMessage; 5 | 6 | public class GrammarInfoMessage extends GrammarSemanticsMessage { 7 | public GrammarInfoMessage(String fileName, Token offendingToken, Object... args) { 8 | super(null, fileName, offendingToken, args); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/org/antlr/intellij/plugin/validation/GrammarIssue.java: -------------------------------------------------------------------------------- 1 | package org.antlr.intellij.plugin.validation; 2 | 3 | import org.antlr.runtime.Token; 4 | import org.antlr.v4.tool.ANTLRMessage; 5 | 6 | import java.util.ArrayList; 7 | import java.util.List; 8 | 9 | public class GrammarIssue { 10 | private String annotation; 11 | private final List offendingTokens = new ArrayList<>(); 12 | private final ANTLRMessage msg; 13 | 14 | public GrammarIssue(ANTLRMessage msg) { this.msg = msg; } 15 | 16 | public String getAnnotation() { 17 | return annotation; 18 | } 19 | 20 | public void setAnnotation(String annotation) { 21 | this.annotation = annotation; 22 | } 23 | 24 | public List getOffendingTokens() { 25 | return offendingTokens; 26 | } 27 | 28 | public ANTLRMessage getMsg() { 29 | return msg; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/org/antlr/intellij/plugin/validation/GrammarIssuesCollectorToolListener.java: -------------------------------------------------------------------------------- 1 | package org.antlr.intellij.plugin.validation; 2 | 3 | import org.antlr.v4.tool.ANTLRMessage; 4 | import org.antlr.v4.tool.ANTLRToolListener; 5 | 6 | import java.util.ArrayList; 7 | import java.util.List; 8 | 9 | /** Track all issues found in a grammar for use by the external 10 | * annotator that puts up messages in grammar editor window. 11 | * The annotator looks for semantic errors not syntax errors, 12 | * which are indicated with error nodes in the PSI. 13 | */ 14 | class GrammarIssuesCollectorToolListener implements ANTLRToolListener { 15 | private final List issues = new ArrayList<>(); 16 | 17 | @Override 18 | public void info(String msg) { 19 | } 20 | 21 | @Override 22 | public void error(ANTLRMessage msg) { 23 | issues.add(new GrammarIssue(msg)); 24 | } 25 | 26 | @Override 27 | public void warning(ANTLRMessage msg) { 28 | issues.add(new GrammarIssue(msg)); 29 | } 30 | 31 | public List getIssues() { 32 | return issues; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/resources/META-INF/pluginIcon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/main/resources/colorSchemes/ANTLRv4Darcula.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 13 | -------------------------------------------------------------------------------- /src/main/resources/colorSchemes/ANTLRv4Default.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 13 | -------------------------------------------------------------------------------- /src/main/resources/icons/org/antlr/intellij/plugin/antlr-icon.idraw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antlr/intellij-plugin-v4/46bc9a5d2bdd88d0d2144e5cd26f15ba8bf409d2/src/main/resources/icons/org/antlr/intellij/plugin/antlr-icon.idraw -------------------------------------------------------------------------------- /src/main/resources/icons/org/antlr/intellij/plugin/antlr-tiny-icon.idraw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antlr/intellij-plugin-v4/46bc9a5d2bdd88d0d2144e5cd26f15ba8bf409d2/src/main/resources/icons/org/antlr/intellij/plugin/antlr-tiny-icon.idraw -------------------------------------------------------------------------------- /src/main/resources/icons/org/antlr/intellij/plugin/antlr.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antlr/intellij-plugin-v4/46bc9a5d2bdd88d0d2144e5cd26f15ba8bf409d2/src/main/resources/icons/org/antlr/intellij/plugin/antlr.png -------------------------------------------------------------------------------- /src/main/resources/icons/org/antlr/intellij/plugin/antlr.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/main/resources/icons/org/antlr/intellij/plugin/antlr@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antlr/intellij-plugin-v4/46bc9a5d2bdd88d0d2144e5cd26f15ba8bf409d2/src/main/resources/icons/org/antlr/intellij/plugin/antlr@2x.png -------------------------------------------------------------------------------- /src/main/resources/icons/org/antlr/intellij/plugin/lexer-rule.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antlr/intellij-plugin-v4/46bc9a5d2bdd88d0d2144e5cd26f15ba8bf409d2/src/main/resources/icons/org/antlr/intellij/plugin/lexer-rule.png -------------------------------------------------------------------------------- /src/main/resources/icons/org/antlr/intellij/plugin/lexer-rule.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/main/resources/icons/org/antlr/intellij/plugin/lexer-rule@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antlr/intellij-plugin-v4/46bc9a5d2bdd88d0d2144e5cd26f15ba8bf409d2/src/main/resources/icons/org/antlr/intellij/plugin/lexer-rule@2x.png -------------------------------------------------------------------------------- /src/main/resources/icons/org/antlr/intellij/plugin/mode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antlr/intellij-plugin-v4/46bc9a5d2bdd88d0d2144e5cd26f15ba8bf409d2/src/main/resources/icons/org/antlr/intellij/plugin/mode.png -------------------------------------------------------------------------------- /src/main/resources/icons/org/antlr/intellij/plugin/mode.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/main/resources/icons/org/antlr/intellij/plugin/mode@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antlr/intellij-plugin-v4/46bc9a5d2bdd88d0d2144e5cd26f15ba8bf409d2/src/main/resources/icons/org/antlr/intellij/plugin/mode@2x.png -------------------------------------------------------------------------------- /src/main/resources/icons/org/antlr/intellij/plugin/parser-rule.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antlr/intellij-plugin-v4/46bc9a5d2bdd88d0d2144e5cd26f15ba8bf409d2/src/main/resources/icons/org/antlr/intellij/plugin/parser-rule.png -------------------------------------------------------------------------------- /src/main/resources/icons/org/antlr/intellij/plugin/parser-rule.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/main/resources/icons/org/antlr/intellij/plugin/parser-rule@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antlr/intellij-plugin-v4/46bc9a5d2bdd88d0d2144e5cd26f15ba8bf409d2/src/main/resources/icons/org/antlr/intellij/plugin/parser-rule@2x.png -------------------------------------------------------------------------------- /src/main/resources/icons/org/antlr/intellij/plugin/toolWindowAntlr.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/main/resources/icons/org/antlr/intellij/plugin/toolWindowAntlr_dark.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/main/resources/liveTemplates/lexer/user.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 17 | 18 | 24 | 25 | 31 | 32 | 38 | 39 | 45 | 46 | 52 | 53 | 59 | 60 | 66 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /src/main/resources/templates/org/antlr/intellij/plugin/gen/Java.stg: -------------------------------------------------------------------------------- 1 | tokenTypeFile(package, grammarName, tokenTypeClassName, tokenNames, keywords, ruleNames, commentTokens, whitespaceTokens) ::= << 2 | package ; 3 | 4 | import com.intellij.psi.TokenType; 5 | import com.intellij.psi.tree.IElementType; 6 | import com.intellij.psi.tree.TokenSet; 7 | import org.antlr.v4.runtime.Token; 8 | 9 | import org.antlr.intellij.plugin.ANTLRv4TokenType; 10 | 11 | import static .Lexer.*; 12 | 13 | public class TokenTypes { 14 | public static IElementType BAD_CHARACTER = TokenType.BAD_CHARACTER; 15 | public static BAD_TOKEN = new ("BAD_TOKEN"); 16 | public static EOF = new (Token.EOF, "EOF"); 17 | 18 | = new (ANTLRv4Lexer., "");}; separator="\n"> 19 | 20 | = new ("");}; separator="\n"> 21 | 22 | public static TokenSet COMMENTS = TokenSet.create(); 23 | public static TokenSet WHITESPACES = TokenSet.create(BAD_TOKEN, ); 24 | public static TokenSet KEYWORDS = TokenSet.create(); 25 | 26 | public static [] typeToIDEATokenType = new [+1]; 27 | public static [] ruleToIDEATokenType = new [+1]; 28 | 29 | static { 30 | Lexer.] = ;}; separator="\n"> 31 | ] = ;}; separator="\n"> 32 | } 33 | } 34 | >> 35 | -------------------------------------------------------------------------------- /src/test/java/org/antlr/intellij/plugin/ANTLRv4ExternalAnnotatorTest.java: -------------------------------------------------------------------------------- 1 | package org.antlr.intellij.plugin; 2 | 3 | import com.google.common.collect.Iterables; 4 | import com.intellij.lang.annotation.Annotation; 5 | import com.intellij.lang.annotation.HighlightSeverity; 6 | import com.intellij.psi.PsiFile; 7 | import org.antlr.intellij.plugin.validation.CreateRuleFix; 8 | import org.antlr.intellij.plugin.validation.AddTokenDefinitionFix; 9 | import org.antlr.intellij.plugin.validation.GrammarIssue; 10 | import org.antlr.v4.tool.ANTLRMessage; 11 | import org.antlr.v4.tool.ErrorType; 12 | import org.junit.Assert; 13 | import org.junit.Test; 14 | import org.mockito.Mockito; 15 | 16 | public class ANTLRv4ExternalAnnotatorTest { 17 | 18 | @Test 19 | public void shouldRegisterTokenDefinitionQuickFix() { 20 | // given: 21 | Annotation annotation = new Annotation(0,0, HighlightSeverity.WARNING, "msg", "tooltip"); 22 | 23 | // when: 24 | ANTLRv4ExternalAnnotator.registerFixForAnnotation(annotation, new GrammarIssue(new ANTLRMessage(ErrorType.IMPLICIT_TOKEN_DEFINITION)), null); 25 | 26 | // then: 27 | Annotation.QuickFixInfo quickFix = Iterables.getOnlyElement(annotation.getQuickFixes()); 28 | Assert.assertTrue(quickFix.quickFix instanceof AddTokenDefinitionFix); 29 | } 30 | 31 | @Test 32 | public void shouldRegisterCreateRuleQuickFix() { 33 | // given: 34 | Annotation annotation = new Annotation(0,0, HighlightSeverity.WARNING, "msg", "tooltip"); 35 | PsiFile file = Mockito.mock(PsiFile.class); 36 | Mockito.when(file.getText()).thenReturn("sample text"); 37 | 38 | // when: 39 | ANTLRv4ExternalAnnotator.registerFixForAnnotation(annotation, new GrammarIssue(new ANTLRMessage(ErrorType.UNDEFINED_RULE_REF)), file); 40 | 41 | // then: 42 | Annotation.QuickFixInfo quickFix = Iterables.getOnlyElement(annotation.getQuickFixes()); 43 | Assert.assertTrue(quickFix.quickFix instanceof CreateRuleFix); 44 | } 45 | } -------------------------------------------------------------------------------- /src/test/java/org/antlr/intellij/plugin/TestUtils.java: -------------------------------------------------------------------------------- 1 | package org.antlr.intellij.plugin; 2 | 3 | import com.intellij.openapi.diagnostic.Attachment; 4 | import com.intellij.openapi.diagnostic.ExceptionWithAttachments; 5 | import com.intellij.util.ThrowableRunnable; 6 | import com.intellij.util.lang.CompoundRuntimeException; 7 | 8 | import java.io.PrintWriter; 9 | import java.io.StringWriter; 10 | 11 | public class TestUtils { 12 | public static void tearDownIgnoringObjectNotDisposedException(ThrowableRunnable delegate) throws Exception { 13 | try { 14 | delegate.run(); 15 | } catch (RuntimeException e) { 16 | // We don't want to release the editor in the Tool Output tool window, so we ignore 17 | // ObjectNotDisposedExceptions related to this particular editor 18 | if (exceptionShouldBeIgnored(e)) { 19 | return; 20 | } 21 | 22 | throw e; 23 | } 24 | } 25 | 26 | private static boolean exceptionShouldBeIgnored(RuntimeException e) { 27 | if (e instanceof CompoundRuntimeException) { 28 | for (Throwable exception : ((CompoundRuntimeException) e).getExceptions()) { 29 | if (exception instanceof RuntimeException && exceptionShouldBeIgnored((RuntimeException) exception)) { 30 | return true; 31 | } 32 | } 33 | } 34 | 35 | if (e.getClass().getName().equals("com.intellij.openapi.util.TraceableDisposable$ObjectNotDisposedException")) { 36 | StringWriter stringWriter = new StringWriter(); 37 | e.printStackTrace(new PrintWriter(stringWriter)); 38 | String stack = stringWriter.toString(); 39 | 40 | if (stackMatchesOurEditorCreation(stack)) { 41 | return true; 42 | } 43 | } 44 | 45 | if (e.getClass().getName().equals("com.intellij.openapi.util.TraceableDisposable$DisposalException")) { 46 | for (Attachment attachment : ((ExceptionWithAttachments) e).getAttachments()) { 47 | if (stackMatchesOurEditorCreation(attachment.getDisplayText())) { 48 | return true; 49 | } 50 | } 51 | } 52 | 53 | return false; 54 | } 55 | 56 | private static boolean stackMatchesOurEditorCreation(String stack) { 57 | return stack.contains("ANTLRv4PluginController.createToolWindows") 58 | || stack.contains("Issue559Test") 59 | || stack.contains("Issue540Test") 60 | || stack.contains("org.antlr.intellij.plugin.preview.InputPanel.createPreviewEditor"); 61 | } 62 | } 63 | 64 | 65 | -------------------------------------------------------------------------------- /src/test/java/org/antlr/intellij/plugin/actions/AnnotationIntentActionsFactoryTest.java: -------------------------------------------------------------------------------- 1 | package org.antlr.intellij.plugin.actions; 2 | 3 | import com.intellij.codeInsight.intention.IntentionAction; 4 | import com.intellij.openapi.util.TextRange; 5 | import org.antlr.intellij.plugin.psi.LexerRuleRefNode; 6 | import org.antlr.v4.tool.ErrorType; 7 | import org.junit.Test; 8 | 9 | import java.util.Optional; 10 | 11 | import static org.junit.Assert.assertFalse; 12 | import static org.mockito.Mockito.mock; 13 | import static org.mockito.Mockito.when; 14 | 15 | public class AnnotationIntentActionsFactoryTest { 16 | @Test 17 | public void shouldReturnEmptyOptionalForUnsupportedErrorType() { 18 | // when: 19 | LexerRuleRefNode lexerRuleRefNode = mock(LexerRuleRefNode.class); 20 | when(lexerRuleRefNode.isValid()).thenReturn(true); 21 | Optional quickFix = AnnotationIntentActionsFactory.getFix(new TextRange(0, 1), ErrorType.INTERNAL_ERROR, null); 22 | 23 | // then: 24 | assertFalse(quickFix.isPresent()); 25 | } 26 | } -------------------------------------------------------------------------------- /src/test/java/org/antlr/intellij/plugin/configdialogs/ANTLRv4GrammarPropertiesStoreTest.java: -------------------------------------------------------------------------------- 1 | package org.antlr.intellij.plugin.configdialogs; 2 | 3 | import org.junit.Assert; 4 | import org.junit.Test; 5 | 6 | public class ANTLRv4GrammarPropertiesStoreTest { 7 | 8 | private static final String MY_GRAMMAR_PATH = "/home/grammars/test/MyGrammar.java"; 9 | 10 | @Test 11 | public void shouldReturnPropertiesForExactFile() { 12 | // given: 13 | ANTLRv4GrammarPropertiesStore propertiesStore = new ANTLRv4GrammarPropertiesStore(); 14 | propertiesStore.add(createGrammarProperties("/home/grammars/test/NotMyGrammar.java")); 15 | ANTLRv4GrammarProperties myGrammarProperties = createGrammarProperties(MY_GRAMMAR_PATH); 16 | propertiesStore.add(myGrammarProperties); 17 | 18 | // when: 19 | ANTLRv4GrammarProperties grammarProperties = propertiesStore.getGrammarProperties(MY_GRAMMAR_PATH); 20 | 21 | // then: 22 | Assert.assertSame(grammarProperties, myGrammarProperties); 23 | } 24 | 25 | @Test 26 | public void shouldReturnDefaultPropertiesIfNoneDefined() { 27 | // given: 28 | ANTLRv4GrammarPropertiesStore propertiesStore = new ANTLRv4GrammarPropertiesStore(); 29 | 30 | // when: 31 | ANTLRv4GrammarProperties grammarProperties = propertiesStore.getGrammarProperties(MY_GRAMMAR_PATH); 32 | 33 | // then: 34 | Assert.assertSame(grammarProperties, ANTLRv4GrammarPropertiesStore.DEFAULT_GRAMMAR_PROPERTIES); 35 | } 36 | 37 | @Test 38 | public void shouldMatchPropertiesByWildcard() { 39 | // given: 40 | ANTLRv4GrammarPropertiesStore propertiesStore = new ANTLRv4GrammarPropertiesStore(); 41 | propertiesStore.add(createGrammarProperties("*/main/*.java")); 42 | ANTLRv4GrammarProperties testGrammarProperties = createGrammarProperties("/home/*/test/*.java"); 43 | propertiesStore.add(testGrammarProperties); 44 | 45 | // when: 46 | ANTLRv4GrammarProperties grammarProperties = propertiesStore.getGrammarProperties(MY_GRAMMAR_PATH); 47 | 48 | // then: 49 | Assert.assertSame(grammarProperties, testGrammarProperties); 50 | } 51 | 52 | @Test 53 | public void shouldPreferExactMatchOverWildcard() { 54 | // given: 55 | ANTLRv4GrammarPropertiesStore propertiesStore = new ANTLRv4GrammarPropertiesStore(); 56 | propertiesStore.add(createGrammarProperties("/home/grammars/test/NotMyGrammar.java")); 57 | propertiesStore.add(createGrammarProperties("/home/*/test/*.java")); 58 | ANTLRv4GrammarProperties myGrammarProperties = createGrammarProperties(MY_GRAMMAR_PATH); 59 | propertiesStore.add(myGrammarProperties); 60 | 61 | // when: 62 | ANTLRv4GrammarProperties grammarProperties = propertiesStore.getGrammarProperties(MY_GRAMMAR_PATH); 63 | 64 | // then: 65 | Assert.assertSame(grammarProperties, myGrammarProperties); 66 | } 67 | 68 | private ANTLRv4GrammarProperties createGrammarProperties(String fileName) { 69 | ANTLRv4GrammarProperties antlRv4GrammarProperties = new ANTLRv4GrammarProperties(); 70 | antlRv4GrammarProperties.fileName = fileName; 71 | return antlRv4GrammarProperties; 72 | } 73 | } -------------------------------------------------------------------------------- /src/test/java/org/antlr/intellij/plugin/configdialogs/ANTLRv4GrammarPropertiesTest.java: -------------------------------------------------------------------------------- 1 | package org.antlr.intellij.plugin.configdialogs; 2 | 3 | import org.junit.Assert; 4 | import org.junit.Before; 5 | import org.junit.Test; 6 | 7 | public class ANTLRv4GrammarPropertiesTest { 8 | 9 | private static final String QUAL_FILE_NAME = "file"; 10 | private static final String FILE_VALUE = "fileValue"; 11 | private static final String PROJECT_VALUE = "projectValue"; 12 | private static final String DEFAULT_VALUE = ""; 13 | 14 | private ANTLRv4GrammarPropertiesStore propertiesStore; 15 | 16 | @Before 17 | public void setUp() { 18 | propertiesStore = new ANTLRv4GrammarPropertiesStore(); 19 | } 20 | 21 | @Test 22 | public void shouldGetPropertyFromFileSettingsWhenDefined() { 23 | // given: 24 | ANTLRv4GrammarProperties fileProps = new ANTLRv4GrammarProperties(); 25 | fileProps.fileName = QUAL_FILE_NAME; 26 | fileProps.language = FILE_VALUE; 27 | propertiesStore.add(fileProps); 28 | 29 | ANTLRv4GrammarProperties projectProps = new ANTLRv4GrammarProperties(); 30 | projectProps.fileName = "*"; 31 | projectProps.language = PROJECT_VALUE; 32 | propertiesStore.add(projectProps); 33 | 34 | // when: 35 | String propertyValueForFile = propertiesStore.getGrammarProperties(QUAL_FILE_NAME).getLanguage(); 36 | 37 | // then: 38 | Assert.assertEquals(FILE_VALUE, propertyValueForFile); 39 | } 40 | 41 | @Test 42 | public void shouldGetPropertyProjectSettingsValueWhenNotSetForFile() { 43 | // given: 44 | ANTLRv4GrammarProperties projectProps = new ANTLRv4GrammarProperties(); 45 | projectProps.fileName = "*"; 46 | projectProps.language = PROJECT_VALUE; 47 | propertiesStore.add(projectProps); 48 | 49 | // when: 50 | String propertyValueForFile = propertiesStore.getGrammarProperties(QUAL_FILE_NAME).getLanguage(); 51 | 52 | // then: 53 | Assert.assertEquals(PROJECT_VALUE, propertyValueForFile); 54 | } 55 | 56 | @Test 57 | public void shouldGetPropertyDefaultValueWhenNotSetForFileNorProject() { 58 | // when: 59 | String propertyValueForFile = propertiesStore.getGrammarProperties(QUAL_FILE_NAME).getLanguage(); 60 | 61 | // then: 62 | Assert.assertEquals(DEFAULT_VALUE, propertyValueForFile); 63 | } 64 | 65 | } -------------------------------------------------------------------------------- /src/test/java/org/antlr/intellij/plugin/configdialogs/ConfigANTLRPerGrammarTest.java: -------------------------------------------------------------------------------- 1 | package org.antlr.intellij.plugin.configdialogs; 2 | 3 | import org.junit.Assert; 4 | import org.junit.Before; 5 | import org.junit.Test; 6 | import org.mockito.Mockito; 7 | 8 | import static org.mockito.Mockito.doReturn; 9 | import static org.mockito.Mockito.when; 10 | 11 | public class ConfigANTLRPerGrammarTest { 12 | 13 | private static final String DEFAULT_OUTPUT_DIR = "DefaultOutputDir"; 14 | private static final String DEFAULT_ENCODING = "DefaultEncoding"; 15 | private static final String DEFAULT_LIBRARY = "DefaultLibrary"; 16 | private static final String DEFAULT_PACKAGE = "DefaultPackage"; 17 | private static final String DEFAULT_LANGUAGE = "DefaultLanguage"; 18 | 19 | private ANTLRv4GrammarProperties originalProperties; 20 | private ConfigANTLRPerGrammar form; 21 | 22 | @Before 23 | public void setUp() { 24 | originalProperties = buildOriginalProperties(); 25 | this.form = mockForm(); 26 | } 27 | 28 | private ConfigANTLRPerGrammar mockForm() { 29 | ConfigANTLRPerGrammar form = Mockito.mock(ConfigANTLRPerGrammar.class, Mockito.CALLS_REAL_METHODS); 30 | doReturn(DEFAULT_OUTPUT_DIR).when(form).getOutputDirText(); 31 | doReturn(DEFAULT_ENCODING).when(form).getFileEncodingText(); 32 | doReturn(DEFAULT_LIBRARY).when(form).getLibDirText(); 33 | doReturn(DEFAULT_PACKAGE).when(form).getPackageFieldText(); 34 | doReturn(DEFAULT_LANGUAGE).when(form).getLanguageText(); 35 | return form; 36 | } 37 | 38 | private ANTLRv4GrammarProperties buildOriginalProperties() { 39 | ANTLRv4GrammarProperties properties = new ANTLRv4GrammarProperties(); 40 | 41 | properties.outputDir = DEFAULT_OUTPUT_DIR; 42 | properties.encoding = DEFAULT_ENCODING; 43 | properties.libDir = DEFAULT_LIBRARY; 44 | properties.pkg = DEFAULT_PACKAGE; 45 | properties.language = DEFAULT_LANGUAGE; 46 | 47 | return properties; 48 | } 49 | 50 | @Test 51 | public void shouldDetectModifiedOutputDirectory() { 52 | // given: 53 | when(form.getOutputDirText()).thenReturn("ModifiedOutputDir"); 54 | 55 | // then: 56 | Assert.assertTrue(form.isModified(originalProperties)); 57 | } 58 | 59 | @Test 60 | public void shouldDetectModifiedLibDirectory() { 61 | // given: 62 | when(form.getLibDirText()).thenReturn("ModifiedLibDir"); 63 | 64 | // then: 65 | Assert.assertTrue(form.isModified(originalProperties)); 66 | } 67 | 68 | @Test 69 | public void shouldDetectModifiedEncoding() { 70 | // given: 71 | when(form.getFileEncodingText()).thenReturn("ModifiedEncoding"); 72 | 73 | // then: 74 | Assert.assertTrue(form.isModified(originalProperties)); 75 | } 76 | 77 | @Test 78 | public void shouldDetectModifiedPackage() { 79 | // given: 80 | when(form.getFileEncodingText()).thenReturn("ModifiedPackage"); 81 | 82 | // then: 83 | Assert.assertTrue(form.isModified(originalProperties)); 84 | } 85 | 86 | @Test 87 | public void shouldDetectModifiedLanguage() { 88 | // given: 89 | when(form.getLanguageText()).thenReturn("ModifiedLanguage"); 90 | 91 | // then: 92 | Assert.assertTrue(form.isModified(originalProperties)); 93 | } 94 | } -------------------------------------------------------------------------------- /src/test/java/org/antlr/intellij/plugin/editor/Issue559Test.java: -------------------------------------------------------------------------------- 1 | package org.antlr.intellij.plugin.editor; 2 | 3 | import com.intellij.openapi.editor.EditorFactory; 4 | import com.intellij.openapi.fileEditor.FileEditorManager; 5 | import com.intellij.openapi.project.Project; 6 | import com.intellij.openapi.vfs.VirtualFile; 7 | import com.intellij.openapi.wm.ToolWindow; 8 | import com.intellij.testFramework.fixtures.BasePlatformTestCase; 9 | import org.antlr.intellij.plugin.ANTLRv4PluginController; 10 | import org.antlr.intellij.plugin.TestUtils; 11 | import org.antlr.intellij.plugin.preview.PreviewPanel; 12 | import org.junit.Test; 13 | import org.junit.Assert; 14 | 15 | public class Issue559Test extends BasePlatformTestCase { 16 | 17 | @Test 18 | public void test_shouldNotClosePanel() { 19 | 20 | Project project = getProject(); 21 | VirtualFile[] vFiles = new VirtualFile[]{openFile("T1.g4"), openFile("T2.g4")}; 22 | 23 | // Setup 24 | ANTLRv4PluginController controller = ANTLRv4PluginController.getInstance(project); 25 | PreviewPanel panel = controller.getPreviewPanel(); 26 | ToolWindow previewWindow = controller.previewWindow = new MockToolWindow(); 27 | FileEditorManager source = FileEditorManager.getInstance(project); 28 | 29 | // Close file 1 30 | controller.myFileEditorManagerAdapter.fileClosed(source, vFiles[0]); 31 | Assert.assertTrue(previewWindow.isVisible()); 32 | Assert.assertTrue(panel.isEnabled()); 33 | 34 | // Close file 2 35 | controller.myFileEditorManagerAdapter.fileClosed(source, vFiles[1]); 36 | Assert.assertFalse(previewWindow.isVisible()); 37 | Assert.assertFalse(panel.isEnabled()); 38 | 39 | } 40 | 41 | private VirtualFile openFile(String file) { 42 | VirtualFile vf = myFixture.configureByFile(file).getVirtualFile(); 43 | myFixture.openFileInEditor(vf); 44 | return vf; 45 | } 46 | 47 | @Override 48 | protected String getTestDataPath() { 49 | return "src/test/resources/editor"; 50 | } 51 | 52 | @Override 53 | protected void tearDown() throws Exception { 54 | TestUtils.tearDownIgnoringObjectNotDisposedException(() -> { 55 | EditorFactory.getInstance().releaseEditor(myFixture.getEditor()); 56 | super.tearDown(); 57 | }); 58 | } 59 | 60 | } -------------------------------------------------------------------------------- /src/test/java/org/antlr/intellij/plugin/folding/ANTLRv4FoldingBuilderTest.java: -------------------------------------------------------------------------------- 1 | package org.antlr.intellij.plugin.folding; 2 | 3 | import com.intellij.codeInsight.folding.CodeFoldingManager; 4 | import com.intellij.openapi.editor.Editor; 5 | import com.intellij.openapi.editor.FoldRegion; 6 | import com.intellij.testFramework.EditorTestUtil; 7 | import com.intellij.testFramework.fixtures.LightPlatformCodeInsightFixtureTestCase; 8 | import org.antlr.intellij.plugin.TestUtils; 9 | 10 | import java.lang.reflect.Method; 11 | 12 | public class ANTLRv4FoldingBuilderTest extends LightPlatformCodeInsightFixtureTestCase { 13 | 14 | public void test_folding_should_not_throw_on_incomplete_prequel() { 15 | // Given 16 | myFixture.configureByText("foo.g4", "grammar foo;\n @\n"); 17 | 18 | // When 19 | buildInitialFoldings(); 20 | 21 | // Then 22 | FoldRegion[] allFoldRegions = myFixture.getEditor().getFoldingModel().getAllFoldRegions(); 23 | assertEquals(0, allFoldRegions.length); 24 | } 25 | 26 | public void test_should_not_fold_single_line() { 27 | // Given 28 | myFixture.configureByText("foo.g4", "grammar foo;\n @members { int i; }\n"); 29 | 30 | // When 31 | buildInitialFoldings(); 32 | 33 | // Then 34 | FoldRegion[] allFoldRegions = myFixture.getEditor().getFoldingModel().getAllFoldRegions(); 35 | assertEquals(0, allFoldRegions.length); 36 | } 37 | 38 | @Override 39 | protected void tearDown() throws Exception { 40 | TestUtils.tearDownIgnoringObjectNotDisposedException(() -> super.tearDown()); 41 | } 42 | 43 | private void buildInitialFoldings() { 44 | try { 45 | Method method = EditorTestUtil.class.getMethod("buildInitialFoldingsInBackground", Editor.class); 46 | method.invoke(null, myFixture.getEditor()); 47 | } catch (ReflectiveOperationException e) { 48 | CodeFoldingManager.getInstance(getProject()).buildInitialFoldings(myFixture.getEditor()); 49 | } 50 | } 51 | } -------------------------------------------------------------------------------- /src/test/java/org/antlr/intellij/plugin/parsing/Issue374Test.java: -------------------------------------------------------------------------------- 1 | package org.antlr.intellij.plugin.parsing; 2 | 3 | import junit.framework.TestCase; 4 | import org.antlr.intellij.adaptor.parser.SyntaxErrorListener; 5 | import org.antlr.intellij.plugin.parser.ANTLRv4Lexer; 6 | import org.antlr.intellij.plugin.parser.ANTLRv4Parser; 7 | import org.antlr.v4.runtime.CharStream; 8 | import org.antlr.v4.runtime.CharStreams; 9 | import org.antlr.v4.runtime.CommonTokenStream; 10 | 11 | public class Issue374Test extends TestCase { 12 | 13 | public void test_lexer_rule_should_be_parsed_after_header() { 14 | // Given 15 | String grammar = "grammar Sample;\n" + 16 | "@header {}\n" + 17 | "WS: [ \\t\\r\\n]+ -> skip ;"; 18 | 19 | ANTLRv4Parser parser = createParser(grammar); 20 | 21 | SyntaxErrorListener listener = new SyntaxErrorListener(); 22 | parser.addErrorListener(listener); 23 | 24 | // When 25 | parser.grammarSpec(); 26 | 27 | // Then 28 | assertTrue(listener.getSyntaxErrors().isEmpty()); 29 | } 30 | 31 | public void test_lexer_rule_should_be_parsed_after_options() { 32 | // Given 33 | String grammar = "grammar Sample;\n" + 34 | "options {" + 35 | " foo = {};" + 36 | "}\n" + 37 | "WS: [ \\t\\r\\n]+ -> skip ;"; 38 | 39 | ANTLRv4Parser parser = createParser(grammar); 40 | 41 | SyntaxErrorListener listener = new SyntaxErrorListener(); 42 | parser.addErrorListener(listener); 43 | 44 | // When 45 | parser.grammarSpec(); 46 | 47 | // Then 48 | assertTrue(listener.getSyntaxErrors().isEmpty()); 49 | } 50 | 51 | public void test_lexer_rule_should_be_parsed_after_tokens() { 52 | // Given 53 | String grammar = "grammar Sample;\n" + 54 | "tokens {" + 55 | " foo, bar" + 56 | "}\n" + 57 | "WS: [ \\t\\r\\n]+ -> skip ;"; 58 | 59 | ANTLRv4Parser parser = createParser(grammar); 60 | 61 | SyntaxErrorListener listener = new SyntaxErrorListener(); 62 | parser.addErrorListener(listener); 63 | 64 | // When 65 | parser.grammarSpec(); 66 | 67 | // Then 68 | assertTrue(listener.getSyntaxErrors().isEmpty()); 69 | } 70 | 71 | public void test_lexer_rule_should_be_parsed_after_channels() { 72 | // Given 73 | String grammar = "grammar Sample;\n" + 74 | "channels {" + 75 | " foo, bar" + 76 | "}\n" + 77 | "WS: [ \\t\\r\\n]+ -> skip ;"; 78 | 79 | ANTLRv4Parser parser = createParser(grammar); 80 | 81 | SyntaxErrorListener listener = new SyntaxErrorListener(); 82 | parser.addErrorListener(listener); 83 | 84 | // When 85 | parser.grammarSpec(); 86 | 87 | // Then 88 | assertTrue(listener.getSyntaxErrors().isEmpty()); 89 | } 90 | 91 | public void test_parser_rule_allows_options() { 92 | // Given 93 | String grammar = "parser grammar Sample;\n" + 94 | "options { key = value; }\n" + 95 | "entry\n" + 96 | "options { key = value; }\n" + 97 | ": 'text' EOF ;"; 98 | 99 | ANTLRv4Parser parser = createParser(grammar); 100 | 101 | SyntaxErrorListener listener = new SyntaxErrorListener(); 102 | ANTLRv4Lexer lexer = (ANTLRv4Lexer) parser.getInputStream().getTokenSource(); 103 | lexer.addErrorListener(listener); 104 | parser.addErrorListener(listener); 105 | 106 | // When 107 | parser.grammarSpec(); 108 | 109 | // Then 110 | assertTrue(listener.getSyntaxErrors().isEmpty()); 111 | } 112 | 113 | private ANTLRv4Parser createParser(String grammar) { 114 | CharStream charStream = CharStreams.fromString(grammar); 115 | ANTLRv4Lexer lexer = new ANTLRv4Lexer(charStream); 116 | CommonTokenStream tokenStream = new CommonTokenStream(lexer); 117 | 118 | return new ANTLRv4Parser(tokenStream); 119 | } 120 | } -------------------------------------------------------------------------------- /src/test/java/org/antlr/intellij/plugin/parsing/RunANTLROnGrammarFileTest.java: -------------------------------------------------------------------------------- 1 | package org.antlr.intellij.plugin.parsing; 2 | 3 | import com.intellij.openapi.vfs.VirtualFile; 4 | import com.intellij.testFramework.LightPlatformCodeInsightTestCase; 5 | import com.intellij.testFramework.VfsTestUtil; 6 | import org.antlr.intellij.plugin.TestUtils; 7 | 8 | import java.util.List; 9 | 10 | public class RunANTLROnGrammarFileTest extends LightPlatformCodeInsightTestCase { 11 | 12 | public void testPackageOptionShouldNotBeAddedIfDeclaredInHeader() { 13 | VirtualFile file = VfsTestUtil.createFile(getSourceRoot(), "mypkg/myGrammarWithHeader.g4", 14 | "grammar myGrammarWithHeader;\n@header { package com.foo.bar; }\nFOO: 'foo';"); 15 | List options = RunANTLROnGrammarFile.getANTLRArgsAsList(getProject(), file); 16 | 17 | assertFalse(options.contains("-package")); 18 | } 19 | 20 | public void testPackageOptionShouldBeAddedIfNotDeclaredInHeader() { 21 | VirtualFile file = VfsTestUtil.createFile(getSourceRoot(), "mypkg/myGrammarWithoutHeader.g4", 22 | "grammar myGrammarWithoutHeader; FOO: 'foo';"); 23 | List options = RunANTLROnGrammarFile.getANTLRArgsAsList(getProject(), file); 24 | 25 | assertTrue(options.contains("-package")); 26 | assertTrue(options.contains("mypkg")); 27 | } 28 | 29 | @Override 30 | protected void tearDown() throws Exception { 31 | TestUtils.tearDownIgnoringObjectNotDisposedException(() -> super.tearDown()); 32 | } 33 | 34 | } -------------------------------------------------------------------------------- /src/test/java/org/antlr/intellij/plugin/validation/AddTokenDefinitionFixTest.java: -------------------------------------------------------------------------------- 1 | package org.antlr.intellij.plugin.validation; 2 | 3 | import com.intellij.openapi.util.TextRange; 4 | import com.intellij.psi.PsiElement; 5 | import org.junit.Assert; 6 | import org.junit.Test; 7 | import org.mockito.Mockito; 8 | 9 | public class AddTokenDefinitionFixTest { 10 | 11 | private static final int TOKEN_EXPR_START_OFFSET = 10; 12 | private static final int ELEMENT_LENGTH = 20; 13 | 14 | @Test 15 | public void shouldCreateTokenDefinitionText() { 16 | Assert.assertEquals("M Y T O K E N", AddTokenDefinitionFix.buildTokenDefinitionExpressionText("MYTOKEN")); 17 | } 18 | 19 | @Test 20 | public void shouldCreateTokenDefinitionTextWhenHavingLowercase() { 21 | Assert.assertEquals("M Y T O K E N", AddTokenDefinitionFix.buildTokenDefinitionExpressionText("MYToken")); 22 | } 23 | 24 | @Test 25 | public void shouldCreateTokenDefinitionTextWhenHavingNonLetterLiterals() { 26 | Assert.assertEquals("M Y '_' T O K E N", AddTokenDefinitionFix.buildTokenDefinitionExpressionText("MY_TOKEN")); 27 | } 28 | 29 | @Test 30 | public void shouldGetRangeForReplacement() { 31 | // given: 32 | PsiElement element = Mockito.mock(PsiElement.class); 33 | Mockito.when(element.getTextLength()).thenReturn(ELEMENT_LENGTH); 34 | 35 | // when: 36 | TextRange range = AddTokenDefinitionFix.getRange(element, TOKEN_EXPR_START_OFFSET); 37 | 38 | // then: 39 | Assert.assertEquals(TOKEN_EXPR_START_OFFSET, range.getStartOffset()); 40 | Assert.assertEquals(ELEMENT_LENGTH - 1, range.getEndOffset()); 41 | } 42 | } -------------------------------------------------------------------------------- /src/test/java/org/antlr/intellij/plugin/validation/CreateRuleFixTest.java: -------------------------------------------------------------------------------- 1 | package org.antlr.intellij.plugin.validation; 2 | 3 | import com.intellij.openapi.application.ApplicationManager; 4 | import com.intellij.openapi.util.TextRange; 5 | import com.intellij.testFramework.LightPlatformCodeInsightTestCase; 6 | import org.antlr.intellij.plugin.TestUtils; 7 | 8 | public class CreateRuleFixTest extends LightPlatformCodeInsightTestCase { 9 | 10 | public CreateRuleFixTest() { 11 | myTestDataPath = "src/test/resources/quickfixes/CreateRuleFix/"; 12 | } 13 | 14 | public void testQuickFixDescriptionShouldShowRuleName() { 15 | // Given 16 | configureByFile("missingRule.g4"); 17 | CreateRuleFix createRuleFix = new CreateRuleFix(TextRange.create(30, 37), getFile()); 18 | 19 | // When 20 | String description = createRuleFix.getText(); 21 | 22 | // Then 23 | assertEquals("Create rule 'newRule'", description); 24 | } 25 | 26 | public void testQuickFixShouldCreateRuleAfterCurrentRule() { 27 | // Given 28 | configureByFile("missingRule.g4"); 29 | CreateRuleFix createRuleFix = new CreateRuleFix(TextRange.create(30, 37), getFile()); 30 | 31 | // When 32 | ApplicationManager.getApplication().runWriteAction( 33 | () -> createRuleFix.invoke(getProject(), getEditor(), getFile()) 34 | ); 35 | 36 | // Then 37 | assertEquals( 38 | "grammar missingRule;\n\n" + 39 | "myRule: newRule;\n\n" + 40 | "newRule: ' ';", 41 | getFile().getText() 42 | ); 43 | } 44 | 45 | @Override 46 | protected void tearDown() throws Exception { 47 | TestUtils.tearDownIgnoringObjectNotDisposedException(() -> super.tearDown()); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/test/resources/editor/T1.g4: -------------------------------------------------------------------------------- 1 | grammar T1; 2 | startRule : TEST EOF; 3 | TEST : 'TEST'; 4 | WS : [ \t\f;]+ -> skip; -------------------------------------------------------------------------------- /src/test/resources/editor/T2.g4: -------------------------------------------------------------------------------- 1 | grammar T2; 2 | startRule : TEST2 EOF; 3 | TEST2 : 'TEST2'; 4 | WS : [ \t\f;]+ -> skip; -------------------------------------------------------------------------------- /src/test/resources/quickfixes/CreateRuleFix/missingRule.g4: -------------------------------------------------------------------------------- 1 | grammar missingRule; 2 | 3 | myRule: newRule; -------------------------------------------------------------------------------- /src/test/resources/references/FooLexer.g4: -------------------------------------------------------------------------------- 1 | lexer grammar FooLexer; 2 | 3 | tokens { STRING } 4 | channels { MYHIDDEN } 5 | 6 | TOKEN1: 'TOKEN1'; 7 | 8 | fragment Fragment1 9 | : Fragment2 10 | | TOKEN1 11 | ; 12 | 13 | fragment Fragment2 14 | : 'FOO' 15 | | 'BAR' 16 | ; 17 | 18 | SINGLE : '\'' .*? '\'' -> type(STRING), channel(MYHIDDEN); 19 | 20 | -------------------------------------------------------------------------------- /src/test/resources/references/FooParser.g4: -------------------------------------------------------------------------------- 1 | parser grammar FooParser; 2 | 3 | options { 4 | tokenVocab=FooLexer; 5 | } 6 | 7 | myrule: TOKEN1 Fragment1 MYHIDDEN STRING; -------------------------------------------------------------------------------- /src/test/resources/references/FooParser2.g4: -------------------------------------------------------------------------------- 1 | parser grammar FooParser2; 2 | 3 | options { 4 | tokenVocab='FooLexer'; 5 | } 6 | 7 | myrule: TOKEN1 Fragment1 MYHIDDEN STRING; -------------------------------------------------------------------------------- /src/test/resources/references/Modes.g4: -------------------------------------------------------------------------------- 1 | lexer grammar Modes; 2 | 3 | tokens { T1 } 4 | channels { C1 } 5 | 6 | TOKEN1: 'token1' -> pushMode(MY_MODE); 7 | 8 | mode MY_MODE; 9 | 10 | TOKEN2: 'token2' -> type(TOKEN1); 11 | 12 | mode MY_OTHER_MODE; 13 | 14 | TOKEN3: 'token3' -> type(TOKEN2), type(T1), channel(C1), pushMode(MY_MODE); -------------------------------------------------------------------------------- /src/test/resources/references/SimpleGrammar.g4: -------------------------------------------------------------------------------- 1 | grammar SimpleGrammar; 2 | 3 | TOKEN1: '$' DIGIT+ '$'; 4 | 5 | DIGIT: [0-9]+; 6 | 7 | rule1: TOKEN1; 8 | 9 | rule2: DIGIT rule1 DIGIT; 10 | -------------------------------------------------------------------------------- /src/test/resources/references/SimpleGrammar2.g4: -------------------------------------------------------------------------------- 1 | grammar SimpleGrammar; 2 | 3 | TOKEN1: '$' DIGIT+ '$'; 4 | 5 | DIGIT: [0-9]+; 6 | 7 | rule1: TOKEN1; 8 | 9 | rule2: DIGIT rule1 DIGIT; 10 | -------------------------------------------------------------------------------- /src/test/resources/references/imported.g4: -------------------------------------------------------------------------------- 1 | lexer grammar imported; 2 | 3 | import imported2, imported3; 4 | 5 | fragment Foo: 'foo'; -------------------------------------------------------------------------------- /src/test/resources/references/imported2.g4: -------------------------------------------------------------------------------- 1 | lexer grammar imported2; 2 | 3 | fragment Bar: 'bar'; 4 | -------------------------------------------------------------------------------- /src/test/resources/references/imported3.g4: -------------------------------------------------------------------------------- 1 | lexer grammar imported3; 2 | 3 | fragment Bar: 'bar'; 4 | 5 | fragment Baz: 'baz'; 6 | -------------------------------------------------------------------------------- /src/test/resources/references/importing.g4: -------------------------------------------------------------------------------- 1 | lexer grammar importing; 2 | 3 | import imported; 4 | 5 | MY_FOO: Foo; 6 | MY_BAR: Bar; 7 | MY_BAZ: Baz; 8 | --------------------------------------------------------------------------------