From e6554d064f8646e8b7e29d1a542bfef34a409204 Mon Sep 17 00:00:00 2001 From: Adam Kovari Date: Fri, 4 Jun 2021 14:44:13 +0200 Subject: [PATCH 1/7] Add ability to use the generated parser class instead of the grammar source --- README.md | 5 + historical-contributors-agreement.txt | 2 +- .../plugin/ANTLRv4PluginController.java | 119 +++++++++++++----- .../ANTLRv4GrammarProperties.java | 8 ++ .../configdialogs/ConfigANTLRDialogPanel.form | 53 ++++---- .../configdialogs/ConfigANTLRPerGrammar.java | 6 +- .../intellij/plugin/parsing/ParsingUtils.java | 110 ++++++++++------ .../intellij/plugin/preview/PreviewState.java | 7 ++ 8 files changed, 217 insertions(+), 93 deletions(-) diff --git a/README.md b/README.md index f78ec640..074b4ebe 100644 --- a/README.md +++ b/README.md @@ -64,6 +64,11 @@ because the interpreter is language agnostic. For the same reasons, if your parser and/or lexer classes extend a custom implementation of the base parser/lexer classes, your custom code will *not* be run during live preview. +As of 1.17, this limitation is partially not true. The configuration window of a grammar has a new option +that allows using the generated parser code in the preview. In this case, the grammar must be compiled into +a Java class (just to be on Project's target classpath). Any changes made to such grammar are not immediately +reflected in the preview and the project must be recompiled instead. + ## History See [Releases](https://github.com/antlr/intellij-plugin-v4/releases) diff --git a/historical-contributors-agreement.txt b/historical-contributors-agreement.txt index bfb07c2d..e7f4fee6 100644 --- a/historical-contributors-agreement.txt +++ b/historical-contributors-agreement.txt @@ -64,4 +64,4 @@ YYYY/MM/DD, github id, Full name, email 2019/11/24, nopeslide, Uffke Drechsler, nopeslide@web.de 2020/12/05, roggenbrot, Sascha Dais, sdais@gmx.net 2021/11/04, OleksiiKovalov, Oleksii Kovalov, Oleksii.Kovalov@outlook.com - +2021/06/04, akovari, Adam Kovari, kovariadam@gmail.com diff --git a/src/main/java/org/antlr/intellij/plugin/ANTLRv4PluginController.java b/src/main/java/org/antlr/intellij/plugin/ANTLRv4PluginController.java index 1423c0e6..df5b4945 100644 --- a/src/main/java/org/antlr/intellij/plugin/ANTLRv4PluginController.java +++ b/src/main/java/org/antlr/intellij/plugin/ANTLRv4PluginController.java @@ -1,5 +1,6 @@ package org.antlr.intellij.plugin; +import com.intellij.CommonBundle; import com.intellij.execution.filters.TextConsoleBuilder; import com.intellij.execution.filters.TextConsoleBuilderFactory; import com.intellij.execution.ui.ConsoleView; @@ -16,18 +17,18 @@ import com.intellij.openapi.editor.event.EditorMouseAdapter; import com.intellij.openapi.editor.event.EditorMouseEvent; import com.intellij.openapi.extensions.PluginId; -import com.intellij.openapi.fileEditor.FileDocumentManager; -import com.intellij.openapi.fileEditor.FileEditorManager; -import com.intellij.openapi.fileEditor.FileEditorManagerEvent; -import com.intellij.openapi.fileEditor.FileEditorManagerListener; +import com.intellij.openapi.fileEditor.*; import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.progress.Task; import com.intellij.openapi.progress.util.BackgroundTaskUtil; import com.intellij.openapi.progress.util.ProgressWindow; import com.intellij.openapi.project.Project; +import com.intellij.openapi.roots.OrderEnumerator; +import com.intellij.openapi.ui.Messages; import com.intellij.openapi.util.Key; import com.intellij.openapi.util.SystemInfo; +import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.vfs.VirtualFileAdapter; import com.intellij.openapi.vfs.VirtualFileEvent; @@ -37,22 +38,29 @@ import com.intellij.openapi.wm.ToolWindowManager; import com.intellij.ui.content.Content; import com.intellij.ui.content.ContentFactory; +import com.intellij.util.lang.UrlClassLoader; import com.intellij.util.messages.MessageBusConnection; +import org.antlr.intellij.plugin.configdialogs.ANTLRv4GrammarProperties; +import org.antlr.intellij.plugin.configdialogs.ANTLRv4GrammarPropertiesStore; import org.antlr.intellij.plugin.parsing.ParsingUtils; import org.antlr.intellij.plugin.parsing.RunANTLROnGrammarFile; import org.antlr.intellij.plugin.preview.PreviewPanel; import org.antlr.intellij.plugin.preview.PreviewState; import org.antlr.intellij.plugin.profiler.ProfilerPanel; import org.antlr.v4.parse.ANTLRParser; +import org.antlr.v4.runtime.CharStream; +import org.antlr.v4.runtime.Lexer; +import org.antlr.v4.runtime.Parser; +import org.antlr.v4.runtime.TokenStream; import org.antlr.v4.tool.Grammar; import org.antlr.v4.tool.LexerGrammar; import org.jetbrains.annotations.NotNull; import javax.swing.*; import java.io.File; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.*; /** This object is the controller for the ANTLR plug-in. It receives * events and can send them on to its contained components. For example, @@ -90,6 +98,7 @@ public class ANTLRv4PluginController implements ProjectComponent { public MyFileEditorManagerAdapter myFileEditorManagerAdapter = new MyFileEditorManagerAdapter(); private ProgressIndicator parsingProgressIndicator; + private UrlClassLoader projectClassLoader; public ANTLRv4PluginController(Project project) { this.project = project; @@ -122,6 +131,7 @@ public void projectOpened() { // make sure the tool windows are created early createToolWindows(); installListeners(); + initClassLoader(); } public void createToolWindows() { @@ -199,7 +209,22 @@ public String getComponentName() { return "antlr.ProjectComponent"; } - // ------------------------------ + private void initClassLoader() { + List compiledClassUrls = OrderEnumerator.orderEntries(project).runtimeOnly().classes().usingCache().getPathsList().getPathList(); + final List urls = new ArrayList<>(); + + for (String path : compiledClassUrls) { + try { + urls.add(new File(FileUtil.toSystemIndependentName(path)).toURI().toURL()); + } catch (MalformedURLException e1) { + LOG.error(e1); + } + } + + this.projectClassLoader = UrlClassLoader.build().parent(TokenStream.class.getClassLoader()).urls(urls).get(); + } + + // ------------------------------ public void installListeners() { LOG.info("installListeners "+project.getName()); @@ -387,24 +412,58 @@ private void updateGrammarObjectsFromFile(VirtualFile grammarFile, boolean gener } } - private String updateGrammarObjectsFromFile_(VirtualFile grammarFile) { - String grammarFileName = grammarFile.getPath(); - PreviewState previewState = getPreviewState(grammarFile); - Grammar[] grammars = ParsingUtils.loadGrammars(grammarFile, project); - if (grammars != null) { - synchronized (previewState) { // build atomically - previewState.lg = (LexerGrammar)grammars[0]; - previewState.g = grammars[1]; - } - } - else { - synchronized (previewState) { // build atomically - previewState.lg = null; - previewState.g = null; - } - } - return grammarFileName; - } + private String updateGrammarObjectsFromFile_(VirtualFile grammarFile) { + String grammarFileName = grammarFile.getPath(); + PreviewState previewState = getPreviewState(grammarFile); + Grammar[] grammars = ParsingUtils.loadGrammars(grammarFile, project); + if (grammars != null) { + LexerGrammar lg = (LexerGrammar) grammars[0]; + Grammar g = grammars[1]; + + ANTLRv4GrammarProperties grammarProperties = ANTLRv4GrammarPropertiesStore.getGrammarProperties(project, grammarFile); + if (grammarProperties.isUseGeneratedParserCodeCheckBox()) { + String parserClassName = grammarFile.getNameWithoutExtension(); + String lexerClassName = lg.name; + + Class parserClass = null; + Class lexerClass = null; + + Class tokenStreamClass = null; + Class charStreamClass = null; + + try { + tokenStreamClass = Class.forName(TokenStream.class.getName(), true, this.projectClassLoader); + charStreamClass = Class.forName(CharStream.class.getName(), true, this.projectClassLoader); + + parserClass = (Class) Class.forName(parserClassName, true, this.projectClassLoader); + lexerClass = (Class) Class.forName(lexerClassName, true, this.projectClassLoader); + } catch (ClassNotFoundException e) { + Messages.showErrorDialog(project, "Cannot find class '" + parserClassName + "'", CommonBundle.getErrorTitle()); + } + + try { + synchronized (previewState) { // build atomically + previewState.lg = lg; + previewState.g = g; + if (lexerClass != null) { + previewState.lexerCtor = lexerClass.getDeclaredConstructor(charStreamClass); + } + if (parserClass != null) { + previewState.parserCtor = parserClass.getDeclaredConstructor(tokenStreamClass); + } + } + } catch (NoSuchMethodException e) { + e.printStackTrace(); + } + } else { + synchronized (previewState) { + previewState.lg = lg; + previewState.g = g; + } + } + } + return grammarFileName; + } // TODO there could be multiple grammars importing/tokenVocab'ing this lexer grammar public PreviewState getAssociatedParserIfLexer(String grammarFileName) { @@ -445,10 +504,10 @@ public void parseText(final VirtualFile grammarFile, String inputText) { // System.out.println("PARSE START "+Thread.currentThread().getName()); long start = System.nanoTime(); - previewState.parsingResult = ParsingUtils.parseText( - previewState.g, previewState.lg, previewState.startRuleName, - grammarFile, inputText, project - ); + previewState.parsingResult = ParsingUtils.parseText( + previewState.g, previewState.lg, previewState.lexerCtor, previewState.parserCtor, + previewState.startRuleName, grammarFile, inputText, project + ); // long parseTime_ns = System.nanoTime() - start; // double parseTimeMS = parseTime_ns/(1000.0*1000.0); diff --git a/src/main/java/org/antlr/intellij/plugin/configdialogs/ANTLRv4GrammarProperties.java b/src/main/java/org/antlr/intellij/plugin/configdialogs/ANTLRv4GrammarProperties.java index 879411d7..bfdc2a9d 100644 --- a/src/main/java/org/antlr/intellij/plugin/configdialogs/ANTLRv4GrammarProperties.java +++ b/src/main/java/org/antlr/intellij/plugin/configdialogs/ANTLRv4GrammarProperties.java @@ -78,6 +78,9 @@ public class ANTLRv4GrammarProperties implements Cloneable { @OptionTag(converter = CaseChangingStrategyConverter.class) CaseChangingStrategy caseChangingStrategy = CaseChangingStrategy.LEAVE_AS_IS; + @Property + boolean useGeneratedParserCodeCheckBox = false; + public ANTLRv4GrammarProperties() { } @@ -92,6 +95,7 @@ public ANTLRv4GrammarProperties(ANTLRv4GrammarProperties source) { this.generateListener = source.generateListener; this.generateVisitor = source.generateVisitor; this.caseChangingStrategy = source.caseChangingStrategy; + this.useGeneratedParserCodeCheckBox = source.useGeneratedParserCodeCheckBox; } public boolean shouldAutoGenerateParser() { @@ -126,6 +130,10 @@ public boolean shouldGenerateParseTreeVisitor() { return generateVisitor; } + public boolean isUseGeneratedParserCodeCheckBox() { + return useGeneratedParserCodeCheckBox; + } + public CaseChangingStrategy getCaseChangingStrategy() { return caseChangingStrategy; } diff --git a/src/main/java/org/antlr/intellij/plugin/configdialogs/ConfigANTLRDialogPanel.form b/src/main/java/org/antlr/intellij/plugin/configdialogs/ConfigANTLRDialogPanel.form index 138c607b..f99000c4 100644 --- a/src/main/java/org/antlr/intellij/plugin/configdialogs/ConfigANTLRDialogPanel.form +++ b/src/main/java/org/antlr/intellij/plugin/configdialogs/ConfigANTLRDialogPanel.form @@ -1,16 +1,16 @@
- + - + - + @@ -18,7 +18,7 @@ - + @@ -26,7 +26,7 @@ - + @@ -34,7 +34,7 @@ - + @@ -42,7 +42,7 @@ - + @@ -51,7 +51,7 @@ - + @@ -59,7 +59,7 @@ - + @@ -67,7 +67,7 @@ - + @@ -75,19 +75,19 @@ - + - + - + @@ -95,7 +95,7 @@ - + @@ -103,20 +103,15 @@ - + - - - - - - + @@ -124,10 +119,24 @@ - + + + + + + + + + + + + + + + diff --git a/src/main/java/org/antlr/intellij/plugin/configdialogs/ConfigANTLRPerGrammar.java b/src/main/java/org/antlr/intellij/plugin/configdialogs/ConfigANTLRPerGrammar.java index 5271029a..611e4400 100644 --- a/src/main/java/org/antlr/intellij/plugin/configdialogs/ConfigANTLRPerGrammar.java +++ b/src/main/java/org/antlr/intellij/plugin/configdialogs/ConfigANTLRPerGrammar.java @@ -32,8 +32,9 @@ public class ConfigANTLRPerGrammar extends DialogWrapper { protected JCheckBox autoGenerateParsersCheckBox; protected JTextField languageField; private JComboBox caseTransformation; + private JCheckBox useGeneratedParserCodeCheckBox; - private ConfigANTLRPerGrammar(final Project project) { + private ConfigANTLRPerGrammar(final Project project) { super(project, false); } @@ -79,6 +80,7 @@ public void loadValues(Project project, String qualFileName) { caseTransformation.setSelectedItem(grammarProperties.getCaseChangingStrategy()); generateParseTreeListenerCheckBox.setSelected(grammarProperties.shouldGenerateParseTreeListener()); generateParseTreeVisitorCheckBox.setSelected(grammarProperties.shouldGenerateParseTreeVisitor()); + useGeneratedParserCodeCheckBox.setSelected(grammarProperties.isUseGeneratedParserCodeCheckBox()); } public void saveValues(Project project, String qualFileName) { @@ -93,6 +95,7 @@ public void saveValues(Project project, String qualFileName) { grammarProperties.caseChangingStrategy = getCaseChangingStrategy(); grammarProperties.generateListener = generateParseTreeListenerCheckBox.isSelected(); grammarProperties.generateVisitor = generateParseTreeVisitorCheckBox.isSelected(); + grammarProperties.useGeneratedParserCodeCheckBox = useGeneratedParserCodeCheckBox.isSelected(); } boolean isModified(ANTLRv4GrammarProperties originalProperties) { @@ -139,6 +142,7 @@ public String toString() { return "ConfigANTLRPerGrammar{" + " generateParseTreeListenerCheckBox=" + generateParseTreeListenerCheckBox + ", generateParseTreeVisitorCheckBox=" + generateParseTreeVisitorCheckBox + + ", useGeneratedParserCodeCheckBox=" + useGeneratedParserCodeCheckBox + ", packageField=" + packageField + ", outputDirField=" + outputDirField + ", libDirField=" + libDirField + diff --git a/src/main/java/org/antlr/intellij/plugin/parsing/ParsingUtils.java b/src/main/java/org/antlr/intellij/plugin/parsing/ParsingUtils.java index c28981b9..ddfcd082 100644 --- a/src/main/java/org/antlr/intellij/plugin/parsing/ParsingUtils.java +++ b/src/main/java/org/antlr/intellij/plugin/parsing/ParsingUtils.java @@ -1,13 +1,21 @@ package org.antlr.intellij.plugin.parsing; +import com.intellij.CommonBundle; import com.intellij.execution.ui.ConsoleView; import com.intellij.execution.ui.ConsoleViewContentType; +import com.intellij.openapi.compiler.CompilerManager; import com.intellij.openapi.editor.Document; import com.intellij.openapi.fileEditor.FileDocumentManager; import com.intellij.openapi.project.Project; +import com.intellij.openapi.project.ProjectManager; +import com.intellij.openapi.project.ProjectUtil; +import com.intellij.openapi.roots.OrderEnumerator; +import com.intellij.openapi.ui.Messages; +import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.vfs.LocalFileSystem; import com.intellij.openapi.vfs.VfsUtil; import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.util.lang.UrlClassLoader; import org.antlr.intellij.adaptor.parser.SyntaxErrorListener; import org.antlr.intellij.plugin.ANTLRv4PluginController; import org.antlr.intellij.plugin.PluginIgnoreMissingTokensFileErrorManager; @@ -31,11 +39,16 @@ import org.antlr.v4.tool.LexerGrammar; import org.antlr.v4.tool.Rule; import org.antlr.v4.tool.ast.GrammarRootAST; +import org.codehaus.groovy.antlr.AntlrParserPlugin; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.io.File; import java.io.IOException; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.net.MalformedURLException; +import java.net.URL; import java.util.*; import static org.antlr.intellij.plugin.configdialogs.ANTLRv4GrammarPropertiesStore.getGrammarProperties; @@ -193,64 +206,83 @@ public static ParsingResult parseANTLRGrammar(String text) { return new ParsingResult(parser, t, listener); } - public static ParsingResult parseText(Grammar g, - LexerGrammar lg, - String startRuleName, + public static ParsingResult parseText(final Grammar g, + final LexerGrammar lg, + final Constructor lexerCtor, + final Constructor parserCtor, + final String startRuleName, final VirtualFile grammarFile, - String inputText, - Project project) { + final String inputText, + final Project project) { + if ( g==null || lg==null ) { + ANTLRv4PluginController.LOG.info("parseText can't parse: missing lexer or parser no Grammar object for " + + (grammarFile != null ? grammarFile.getName() : "")); + return null; + } + ANTLRv4GrammarProperties grammarProperties = getGrammarProperties(project, grammarFile); CharStream input = grammarProperties.getCaseChangingStrategy() .applyTo(CharStreams.fromString(inputText, grammarFile.getPath())); - LexerInterpreter lexEngine; - lexEngine = lg.createLexerInterpreter(input); - SyntaxErrorListener syntaxErrorListener = new SyntaxErrorListener(); - lexEngine.removeErrorListeners(); - lexEngine.addErrorListener(syntaxErrorListener); - CommonTokenStream tokens = new TokenStreamSubset(lexEngine); - return parseText(g, lg, startRuleName, grammarFile, syntaxErrorListener, tokens, 0); + + try { + CommonTokenStream tokens; + + LexerInterpreter lexEngine; + lexEngine = lg.createLexerInterpreter(input); + SyntaxErrorListener syntaxErrorListener = new SyntaxErrorListener(); + lexEngine.removeErrorListeners(); + lexEngine.addErrorListener(syntaxErrorListener); + + if (lexerCtor != null) { + Lexer lexer = lexerCtor.newInstance(input); + tokens = new TokenStreamSubset(lexer); + } else { + tokens = new TokenStreamSubset(lexEngine); + } + + Parser parser = null; + + if (parserCtor != null) { + parser = parserCtor.newInstance(tokens); + } + + return parseText(g, tokens, parser, syntaxErrorListener, startRuleName, 0); + } catch (InstantiationException | InvocationTargetException | IllegalAccessException e) { + ANTLRv4PluginController.LOG.error(e); + return null; + } } public static ParsingResult parseText(Grammar g, - LexerGrammar lg, - String startRuleName, - final VirtualFile grammarFile, - SyntaxErrorListener syntaxErrorListener, TokenStream tokens, + Parser parser, + SyntaxErrorListener syntaxErrorListener, + String startRuleName, int startIndex) { - if ( g==null || lg==null ) { - ANTLRv4PluginController.LOG.info("parseText can't parse: missing lexer or parser no Grammar object for " + - (grammarFile != null ? grammarFile.getName() : "")); - return null; - } + tokens.seek(startIndex); - String grammarFileName = g.fileName; - if (!new File(grammarFileName).exists()) { - ANTLRv4PluginController.LOG.info("parseText grammar doesn't exist "+grammarFileName); - return null; - } + ParserInterpreter parserInterpreter; - if ( g==BAD_PARSER_GRAMMAR || lg==BAD_LEXER_GRAMMAR ) { - return null; + if (parser != null) { + parserInterpreter = new PreviewParser(g, parser.getATN(), tokens); + } else { + parserInterpreter = new PreviewParser(g, tokens); } - tokens.seek(startIndex); - - PreviewParser parser = new PreviewParser(g, tokens); - parser.getInterpreter().setPredictionMode(PredictionMode.LL_EXACT_AMBIG_DETECTION); - parser.setProfile(true); + parserInterpreter.getInterpreter().setPredictionMode(PredictionMode.LL_EXACT_AMBIG_DETECTION); + parserInterpreter.setProfile(true); - parser.removeErrorListeners(); - parser.addErrorListener(syntaxErrorListener); + parserInterpreter.removeErrorListeners(); + parserInterpreter.addErrorListener(syntaxErrorListener); - Rule start = g.getRule(startRuleName); - if ( start==null ) { + int startRuleIndex = parserInterpreter.getRuleIndex(startRuleName); + if ( startRuleIndex==-1 ) { return null; // can't find start rule } - ParseTree t = parser.parse(start.index); + ParseTree t = parserInterpreter.parse(startRuleIndex); if ( t!=null ) { - return new ParsingResult(parser, t, syntaxErrorListener); + return new ParsingResult(parserInterpreter, t, syntaxErrorListener); } return null; } diff --git a/src/main/java/org/antlr/intellij/plugin/preview/PreviewState.java b/src/main/java/org/antlr/intellij/plugin/preview/PreviewState.java index 7ff6757c..05204ce4 100644 --- a/src/main/java/org/antlr/intellij/plugin/preview/PreviewState.java +++ b/src/main/java/org/antlr/intellij/plugin/preview/PreviewState.java @@ -5,9 +5,13 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.vfs.VirtualFile; import org.antlr.intellij.plugin.parsing.ParsingResult; +import org.antlr.v4.runtime.Lexer; +import org.antlr.v4.runtime.Parser; import org.antlr.v4.tool.Grammar; import org.antlr.v4.tool.LexerGrammar; +import java.lang.reflect.Constructor; + /** Track everything associated with the state of the preview window. * For each grammar, we need to track an InputPanel (with <= 2 editor objects) * that we will flip to every time we come back to a specific grammar, @@ -33,6 +37,9 @@ public class PreviewState { public ParsingResult parsingResult; + public Constructor parserCtor; + public Constructor lexerCtor; + /** The current input editor (inputEditor or fileEditor) for this grammar * in InputPanel. This can be null when a PreviewState and InputPanel * are created out of sync. Depends on order IDE opens files vs From 5e98cca130d3388c91854762b8a6cf07c91e3ec9 Mon Sep 17 00:00:00 2001 From: Adam Kovari Date: Fri, 4 Jun 2021 14:58:18 +0200 Subject: [PATCH 2/7] Undo some changes in the config form --- .../configdialogs/ConfigANTLRDialogPanel.form | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/main/java/org/antlr/intellij/plugin/configdialogs/ConfigANTLRDialogPanel.form b/src/main/java/org/antlr/intellij/plugin/configdialogs/ConfigANTLRDialogPanel.form index f99000c4..b39c9e18 100644 --- a/src/main/java/org/antlr/intellij/plugin/configdialogs/ConfigANTLRDialogPanel.form +++ b/src/main/java/org/antlr/intellij/plugin/configdialogs/ConfigANTLRDialogPanel.form @@ -10,7 +10,7 @@ - + @@ -18,7 +18,7 @@ - + @@ -26,7 +26,7 @@ - + @@ -34,7 +34,7 @@ - + @@ -42,7 +42,7 @@ - + @@ -51,7 +51,7 @@ - + @@ -59,7 +59,7 @@ - + @@ -67,7 +67,7 @@ - + @@ -75,19 +75,19 @@ - + - + - + @@ -95,7 +95,7 @@ - + @@ -103,7 +103,7 @@ - + @@ -111,7 +111,7 @@ - + @@ -119,7 +119,7 @@ - + From e945f861d843174bb6768a5892edd980b5a4d1a2 Mon Sep 17 00:00:00 2001 From: Adam Kovari Date: Fri, 4 Jun 2021 15:06:45 +0200 Subject: [PATCH 3/7] Simplify updateGrammarObjectsFromFile_ --- .../plugin/ANTLRv4PluginController.java | 54 ++++++++----------- 1 file changed, 21 insertions(+), 33 deletions(-) diff --git a/src/main/java/org/antlr/intellij/plugin/ANTLRv4PluginController.java b/src/main/java/org/antlr/intellij/plugin/ANTLRv4PluginController.java index df5b4945..88b050ab 100644 --- a/src/main/java/org/antlr/intellij/plugin/ANTLRv4PluginController.java +++ b/src/main/java/org/antlr/intellij/plugin/ANTLRv4PluginController.java @@ -58,6 +58,7 @@ import javax.swing.*; import java.io.File; +import java.lang.reflect.Constructor; import java.net.MalformedURLException; import java.net.URL; import java.util.*; @@ -420,47 +421,34 @@ private String updateGrammarObjectsFromFile_(VirtualFile grammarFile) { LexerGrammar lg = (LexerGrammar) grammars[0]; Grammar g = grammars[1]; - ANTLRv4GrammarProperties grammarProperties = ANTLRv4GrammarPropertiesStore.getGrammarProperties(project, grammarFile); + Constructor parserCtor = null; + Constructor lexerCtor = null; + + ANTLRv4GrammarProperties grammarProperties = ANTLRv4GrammarPropertiesStore.getGrammarProperties(project, grammarFile); if (grammarProperties.isUseGeneratedParserCodeCheckBox()) { String parserClassName = grammarFile.getNameWithoutExtension(); String lexerClassName = lg.name; - Class parserClass = null; - Class lexerClass = null; - - Class tokenStreamClass = null; - Class charStreamClass = null; + try { + Class tokenStreamClass = Class.forName(TokenStream.class.getName(), true, this.projectClassLoader); + Class charStreamClass = Class.forName(CharStream.class.getName(), true, this.projectClassLoader); - try { - tokenStreamClass = Class.forName(TokenStream.class.getName(), true, this.projectClassLoader); - charStreamClass = Class.forName(CharStream.class.getName(), true, this.projectClassLoader); - - parserClass = (Class) Class.forName(parserClassName, true, this.projectClassLoader); - lexerClass = (Class) Class.forName(lexerClassName, true, this.projectClassLoader); - } catch (ClassNotFoundException e) { - Messages.showErrorDialog(project, "Cannot find class '" + parserClassName + "'", CommonBundle.getErrorTitle()); - } + Class parserClass = (Class) Class.forName(parserClassName, true, this.projectClassLoader); + Class lexerClass = (Class) Class.forName(lexerClassName, true, this.projectClassLoader); - try { - synchronized (previewState) { // build atomically - previewState.lg = lg; - previewState.g = g; - if (lexerClass != null) { - previewState.lexerCtor = lexerClass.getDeclaredConstructor(charStreamClass); - } - if (parserClass != null) { - previewState.parserCtor = parserClass.getDeclaredConstructor(tokenStreamClass); - } - } - } catch (NoSuchMethodException e) { - e.printStackTrace(); - } - } else { - synchronized (previewState) { - previewState.lg = lg; - previewState.g = g; + parserCtor = parserClass.getDeclaredConstructor(tokenStreamClass); + lexerCtor = lexerClass.getDeclaredConstructor(charStreamClass); + } catch (ClassNotFoundException | NoSuchMethodException e) { + LOG.warn(e); } } + + synchronized (previewState) { + previewState.lg = lg; + previewState.g = g; + previewState.lexerCtor = lexerCtor; + previewState.parserCtor = parserCtor; + } } return grammarFileName; } From ec8a8444824e00497c14a57f2a17ee9aaa00cfcc Mon Sep 17 00:00:00 2001 From: Adam Kovari Date: Fri, 4 Jun 2021 20:52:21 +0200 Subject: [PATCH 4/7] Fix col count in the ui form --- .../intellij/plugin/configdialogs/ConfigANTLRDialogPanel.form | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/antlr/intellij/plugin/configdialogs/ConfigANTLRDialogPanel.form b/src/main/java/org/antlr/intellij/plugin/configdialogs/ConfigANTLRDialogPanel.form index b39c9e18..7b6ac33d 100644 --- a/src/main/java/org/antlr/intellij/plugin/configdialogs/ConfigANTLRDialogPanel.form +++ b/src/main/java/org/antlr/intellij/plugin/configdialogs/ConfigANTLRDialogPanel.form @@ -1,6 +1,6 @@
- + From 45c5a16c770929da58ee7e8870244625e8c48beb Mon Sep 17 00:00:00 2001 From: Adam Kovari Date: Thu, 10 Jun 2021 16:15:13 +0200 Subject: [PATCH 5/7] Added ability to configure parser/lexer class names. --- README.md | 3 +- .../plugin/ANTLRv4PluginController.java | 19 ++++++--- .../ANTLRv4GrammarProperties.java | 16 +++++++ .../configdialogs/ConfigANTLRDialogPanel.form | 42 +++++++++++++++++-- .../configdialogs/ConfigANTLRPerGrammar.java | 36 +++++++++++++++- 5 files changed, 104 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 074b4ebe..0c63cdd8 100644 --- a/README.md +++ b/README.md @@ -67,7 +67,8 @@ base parser/lexer classes, your custom code will *not* be run during live previe As of 1.17, this limitation is partially not true. The configuration window of a grammar has a new option that allows using the generated parser code in the preview. In this case, the grammar must be compiled into a Java class (just to be on Project's target classpath). Any changes made to such grammar are not immediately -reflected in the preview and the project must be recompiled instead. +reflected in the preview and the project must be recompiled instead. It is also possible to specify the name of the +compiler parser/lexer class. By default the name of the grammar is used (parser and lexer grammar). ## History diff --git a/src/main/java/org/antlr/intellij/plugin/ANTLRv4PluginController.java b/src/main/java/org/antlr/intellij/plugin/ANTLRv4PluginController.java index 88b050ab..3ba2e165 100644 --- a/src/main/java/org/antlr/intellij/plugin/ANTLRv4PluginController.java +++ b/src/main/java/org/antlr/intellij/plugin/ANTLRv4PluginController.java @@ -101,6 +101,9 @@ public class ANTLRv4PluginController implements ProjectComponent { private ProgressIndicator parsingProgressIndicator; private UrlClassLoader projectClassLoader; + private Class tokenStreamClass; + private Class charStreamClass; + public ANTLRv4PluginController(Project project) { this.project = project; } @@ -217,12 +220,19 @@ private void initClassLoader() { for (String path : compiledClassUrls) { try { urls.add(new File(FileUtil.toSystemIndependentName(path)).toURI().toURL()); - } catch (MalformedURLException e1) { + } catch (MalformedURLException e1) { LOG.error(e1); } } this.projectClassLoader = UrlClassLoader.build().parent(TokenStream.class.getClassLoader()).urls(urls).get(); + + try { + this.tokenStreamClass = Class.forName(TokenStream.class.getName(), true, this.projectClassLoader); + this.charStreamClass = Class.forName(CharStream.class.getName(), true, this.projectClassLoader); + } catch (ClassNotFoundException e) { + LOG.error(e); + } } // ------------------------------ @@ -426,13 +436,10 @@ private String updateGrammarObjectsFromFile_(VirtualFile grammarFile) { ANTLRv4GrammarProperties grammarProperties = ANTLRv4GrammarPropertiesStore.getGrammarProperties(project, grammarFile); if (grammarProperties.isUseGeneratedParserCodeCheckBox()) { - String parserClassName = grammarFile.getNameWithoutExtension(); - String lexerClassName = lg.name; + String parserClassName = grammarProperties.getGeneratedParserClassName() != null ? grammarProperties.getGeneratedParserClassName() : grammarFile.getNameWithoutExtension(); + String lexerClassName = grammarProperties.getGeneratedLexerClassName() != null ? grammarProperties.getGeneratedLexerClassName() : lg.name; try { - Class tokenStreamClass = Class.forName(TokenStream.class.getName(), true, this.projectClassLoader); - Class charStreamClass = Class.forName(CharStream.class.getName(), true, this.projectClassLoader); - Class parserClass = (Class) Class.forName(parserClassName, true, this.projectClassLoader); Class lexerClass = (Class) Class.forName(lexerClassName, true, this.projectClassLoader); diff --git a/src/main/java/org/antlr/intellij/plugin/configdialogs/ANTLRv4GrammarProperties.java b/src/main/java/org/antlr/intellij/plugin/configdialogs/ANTLRv4GrammarProperties.java index bfdc2a9d..f89cac57 100644 --- a/src/main/java/org/antlr/intellij/plugin/configdialogs/ANTLRv4GrammarProperties.java +++ b/src/main/java/org/antlr/intellij/plugin/configdialogs/ANTLRv4GrammarProperties.java @@ -81,6 +81,12 @@ public class ANTLRv4GrammarProperties implements Cloneable { @Property boolean useGeneratedParserCodeCheckBox = false; + @Property + String generatedParserClassName; + + @Property + String generatedLexerClassName; + public ANTLRv4GrammarProperties() { } @@ -96,6 +102,8 @@ public ANTLRv4GrammarProperties(ANTLRv4GrammarProperties source) { this.generateVisitor = source.generateVisitor; this.caseChangingStrategy = source.caseChangingStrategy; this.useGeneratedParserCodeCheckBox = source.useGeneratedParserCodeCheckBox; + this.generatedParserClassName = source.generatedParserClassName; + this.generatedLexerClassName = source.generatedLexerClassName; } public boolean shouldAutoGenerateParser() { @@ -134,6 +142,14 @@ public boolean isUseGeneratedParserCodeCheckBox() { return useGeneratedParserCodeCheckBox; } + public String getGeneratedParserClassName() { + return generatedParserClassName; + } + + public String getGeneratedLexerClassName() { + return generatedLexerClassName; + } + public CaseChangingStrategy getCaseChangingStrategy() { return caseChangingStrategy; } diff --git a/src/main/java/org/antlr/intellij/plugin/configdialogs/ConfigANTLRDialogPanel.form b/src/main/java/org/antlr/intellij/plugin/configdialogs/ConfigANTLRDialogPanel.form index 7b6ac33d..c7f7a6a5 100644 --- a/src/main/java/org/antlr/intellij/plugin/configdialogs/ConfigANTLRDialogPanel.form +++ b/src/main/java/org/antlr/intellij/plugin/configdialogs/ConfigANTLRDialogPanel.form @@ -1,9 +1,9 @@ - + - + @@ -134,9 +134,45 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/java/org/antlr/intellij/plugin/configdialogs/ConfigANTLRPerGrammar.java b/src/main/java/org/antlr/intellij/plugin/configdialogs/ConfigANTLRPerGrammar.java index 611e4400..ffe65ef8 100644 --- a/src/main/java/org/antlr/intellij/plugin/configdialogs/ConfigANTLRPerGrammar.java +++ b/src/main/java/org/antlr/intellij/plugin/configdialogs/ConfigANTLRPerGrammar.java @@ -33,8 +33,10 @@ public class ConfigANTLRPerGrammar extends DialogWrapper { protected JTextField languageField; private JComboBox caseTransformation; private JCheckBox useGeneratedParserCodeCheckBox; + private JTextField generatedParserClassName; + private JTextField generatedLexerClassName; - private ConfigANTLRPerGrammar(final Project project) { + private ConfigANTLRPerGrammar(final Project project) { super(project, false); } @@ -66,6 +68,15 @@ private void initAntlrFields(Project project, String qualFileName) { libDirField.setTextFieldPreferredWidth(50); loadValues(project, qualFileName); + + useGeneratedParserCodeCheckBox.addActionListener(e -> toggleGeneratedClassNames()); + toggleGeneratedClassNames(); + } + + private void toggleGeneratedClassNames() { + boolean enabled = useGeneratedParserCodeCheckBox.isSelected(); + generatedParserClassName.setEnabled(enabled); + generatedLexerClassName.setEnabled(enabled); } public void loadValues(Project project, String qualFileName) { @@ -81,6 +92,8 @@ public void loadValues(Project project, String qualFileName) { generateParseTreeListenerCheckBox.setSelected(grammarProperties.shouldGenerateParseTreeListener()); generateParseTreeVisitorCheckBox.setSelected(grammarProperties.shouldGenerateParseTreeVisitor()); useGeneratedParserCodeCheckBox.setSelected(grammarProperties.isUseGeneratedParserCodeCheckBox()); + generatedParserClassName.setText(grammarProperties.generatedParserClassName); + generatedLexerClassName.setText(grammarProperties.generatedLexerClassName); } public void saveValues(Project project, String qualFileName) { @@ -96,6 +109,8 @@ public void saveValues(Project project, String qualFileName) { grammarProperties.generateListener = generateParseTreeListenerCheckBox.isSelected(); grammarProperties.generateVisitor = generateParseTreeVisitorCheckBox.isSelected(); grammarProperties.useGeneratedParserCodeCheckBox = useGeneratedParserCodeCheckBox.isSelected(); + grammarProperties.generatedParserClassName = getGeneratedParserClassName(); + grammarProperties.generatedLexerClassName = getGeneratedLexerClassName(); } boolean isModified(ANTLRv4GrammarProperties originalProperties) { @@ -104,7 +119,10 @@ boolean isModified(ANTLRv4GrammarProperties originalProperties) { || !Objects.equals(originalProperties.getEncoding(), getFileEncodingText()) || !Objects.equals(originalProperties.getPackage(), getPackageFieldText()) || !Objects.equals(originalProperties.getLanguage(), getLanguageText()) - || !Objects.equals(originalProperties.caseChangingStrategy, getCaseChangingStrategy()); + || !Objects.equals(originalProperties.caseChangingStrategy, getCaseChangingStrategy()) + || !Objects.equals(originalProperties.isUseGeneratedParserCodeCheckBox(), isUseGeneratedParserCode()) + || !Objects.equals(originalProperties.generatedParserClassName, getGeneratedParserClassName()) + || !Objects.equals(originalProperties.generatedLexerClassName, getGeneratedLexerClassName()); } String getLanguageText() { @@ -127,6 +145,18 @@ String getOutputDirText() { return outputDirField.getText(); } + boolean isUseGeneratedParserCode() { + return useGeneratedParserCodeCheckBox.isSelected(); + } + + String getGeneratedParserClassName() { + return generatedParserClassName.getText(); + } + + String getGeneratedLexerClassName() { + return generatedLexerClassName.getText(); + } + private CaseChangingStrategy getCaseChangingStrategy() { return (CaseChangingStrategy) caseTransformation.getSelectedItem(); } @@ -146,6 +176,8 @@ public String toString() { ", packageField=" + packageField + ", outputDirField=" + outputDirField + ", libDirField=" + libDirField + + ", generatedParserClassName=" + generatedParserClassName + + ", generatedLexerClassName=" + generatedLexerClassName + '}'; } From 1b8a6aea8fdf875745f47aff5e2701b3f6469715 Mon Sep 17 00:00:00 2001 From: Adam Kovari Date: Wed, 23 Mar 2022 14:03:24 +0100 Subject: [PATCH 6/7] support 2021.3 --- build.gradle | 4 +- .../plugin/ANTLRv4PluginController.java | 1009 +++++++++-------- .../intellij/plugin/parsing/ParsingUtils.java | 12 - 3 files changed, 508 insertions(+), 517 deletions(-) diff --git a/build.gradle b/build.gradle index 3761bdd6..89f394d2 100644 --- a/build.gradle +++ b/build.gradle @@ -20,8 +20,8 @@ apply plugin: 'org.jetbrains.intellij' apply plugin: 'antlr' compileJava { - sourceCompatibility = '1.8' - targetCompatibility = '1.8' + sourceCompatibility = '11' + targetCompatibility = '11' } intellij { diff --git a/src/main/java/org/antlr/intellij/plugin/ANTLRv4PluginController.java b/src/main/java/org/antlr/intellij/plugin/ANTLRv4PluginController.java index 3ba2e165..5d5dc8b1 100644 --- a/src/main/java/org/antlr/intellij/plugin/ANTLRv4PluginController.java +++ b/src/main/java/org/antlr/intellij/plugin/ANTLRv4PluginController.java @@ -1,6 +1,5 @@ package org.antlr.intellij.plugin; -import com.intellij.CommonBundle; import com.intellij.execution.filters.TextConsoleBuilder; import com.intellij.execution.filters.TextConsoleBuilderFactory; import com.intellij.execution.ui.ConsoleView; @@ -17,7 +16,10 @@ import com.intellij.openapi.editor.event.EditorMouseAdapter; import com.intellij.openapi.editor.event.EditorMouseEvent; import com.intellij.openapi.extensions.PluginId; -import com.intellij.openapi.fileEditor.*; +import com.intellij.openapi.fileEditor.FileDocumentManager; +import com.intellij.openapi.fileEditor.FileEditorManager; +import com.intellij.openapi.fileEditor.FileEditorManagerEvent; +import com.intellij.openapi.fileEditor.FileEditorManagerListener; import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.progress.Task; @@ -25,7 +27,6 @@ import com.intellij.openapi.progress.util.ProgressWindow; import com.intellij.openapi.project.Project; import com.intellij.openapi.roots.OrderEnumerator; -import com.intellij.openapi.ui.Messages; import com.intellij.openapi.util.Key; import com.intellij.openapi.util.SystemInfo; import com.intellij.openapi.util.io.FileUtil; @@ -63,222 +64,223 @@ import java.net.URL; import java.util.*; -/** This object is the controller for the ANTLR plug-in. It receives - * events and can send them on to its contained components. For example, - * saving the grammar editor or flipping to a new grammar sends an event - * to this object, which forwards on update events to the preview tool window. - * - * The main components are related to the console tool window forever output and - * the main panel of the preview tool window. - * - * This controller also manages the cache of grammar/editor combinations - * needed for the preview window. Updates must be made atomically so that - * the grammars and editors are consistently associated with the same window. +/** + * This object is the controller for the ANTLR plug-in. It receives + * events and can send them on to its contained components. For example, + * saving the grammar editor or flipping to a new grammar sends an event + * to this object, which forwards on update events to the preview tool window. + *

+ * The main components are related to the console tool window forever output and + * the main panel of the preview tool window. + *

+ * This controller also manages the cache of grammar/editor combinations + * needed for the preview window. Updates must be made atomically so that + * the grammars and editors are consistently associated with the same window. */ public class ANTLRv4PluginController implements ProjectComponent { - public static final String PLUGIN_ID = "org.antlr.intellij.plugin"; - - public static final Key EDITOR_MOUSE_LISTENER_KEY = Key.create("EDITOR_MOUSE_LISTENER_KEY"); - public static final Logger LOG = Logger.getInstance("ANTLRv4PluginController"); - - public static final String PREVIEW_WINDOW_ID = "ANTLR Preview"; - public static final String CONSOLE_WINDOW_ID = "Tool Output"; - - public boolean projectIsClosed = false; - - public Project project; - public ConsoleView console; - private ToolWindow consoleWindow; - - public Map grammarToPreviewState = - Collections.synchronizedMap(new HashMap<>()); - private ToolWindow previewWindow; // same for all grammar editor - public PreviewPanel previewPanel; // same for all grammar editor - - public MyVirtualFileAdapter myVirtualFileAdapter = new MyVirtualFileAdapter(); - public MyFileEditorManagerAdapter myFileEditorManagerAdapter = new MyFileEditorManagerAdapter(); - - private ProgressIndicator parsingProgressIndicator; - private UrlClassLoader projectClassLoader; - - private Class tokenStreamClass; - private Class charStreamClass; - - public ANTLRv4PluginController(Project project) { - this.project = project; - } - - public static ANTLRv4PluginController getInstance(Project project) { - if ( project==null ) { - LOG.error("getInstance: project is null"); - return null; - } - ANTLRv4PluginController pc = project.getComponent(ANTLRv4PluginController.class); - if ( pc==null ) { - LOG.error("getInstance: getComponent() for "+project.getName()+" returns null"); - } - return pc; - } - - @Override - public void initComponent() { - } - - @Override - public void projectOpened() { - IdeaPluginDescriptor plugin = PluginManager.getPlugin(PluginId.getId(PLUGIN_ID)); - String version = "unknown"; - if ( plugin!=null ) { - version = plugin.getVersion(); - } - LOG.info("ANTLR 4 Plugin version "+version+", Java version "+ SystemInfo.JAVA_VERSION); - // make sure the tool windows are created early - createToolWindows(); - installListeners(); - initClassLoader(); - } - - public void createToolWindows() { - LOG.info("createToolWindows "+project.getName()); - ToolWindowManager toolWindowManager = ToolWindowManager.getInstance(project); - - previewPanel = new PreviewPanel(project); - - ContentFactory contentFactory = ContentFactory.SERVICE.getInstance(); - - toolWindowManager.invokeLater(() -> { - Content content = contentFactory.createContent(previewPanel, "", false); - content.setCloseable(false); - - previewWindow = toolWindowManager.registerToolWindow(PREVIEW_WINDOW_ID, true, ToolWindowAnchor.BOTTOM); - previewWindow.getContentManager().addContent(content); - previewWindow.setIcon(Icons.getToolWindow()); - }); - - TextConsoleBuilderFactory factory = TextConsoleBuilderFactory.getInstance(); - TextConsoleBuilder consoleBuilder = factory.createBuilder(project); - this.console = consoleBuilder.getConsole(); - - toolWindowManager.invokeLater(() -> { - JComponent consoleComponent = console.getComponent(); - Content content = contentFactory.createContent(consoleComponent, "", false); - content.setCloseable(false); - - consoleWindow = toolWindowManager.registerToolWindow(CONSOLE_WINDOW_ID, true, ToolWindowAnchor.BOTTOM); - consoleWindow.getContentManager().addContent(content); - consoleWindow.setIcon(Icons.getToolWindow()); - }); - } - - @Override - public void projectClosed() { - LOG.info("projectClosed " + project.getName()); - //synchronized ( shutdownLock ) { // They should be called from EDT only so no lock - projectIsClosed = true; - uninstallListeners(); - - console.dispose(); - - for (PreviewState it : grammarToPreviewState.values()) { - previewPanel.inputPanel.releaseEditor(it); - } - - previewPanel = null; - previewWindow = null; - consoleWindow = null; - project = null; - grammarToPreviewState = null; - } - - // seems that intellij can kill and reload a project w/o user knowing. - // a ptr was left around that pointed at a disposed project. led to - // problem in switchGrammar. Probably was a listener still attached and trigger - // editor listeners released in editorReleased() events. - public void uninstallListeners() { - VirtualFileManager.getInstance().removeVirtualFileListener(myVirtualFileAdapter); - - if ( !project.isDisposed() ) { - MessageBusConnection msgBus = project.getMessageBus().connect(project); - msgBus.disconnect(); - } - } - - @Override - public void disposeComponent() { - } - - @NotNull - @Override - public String getComponentName() { - return "antlr.ProjectComponent"; - } - - private void initClassLoader() { - List compiledClassUrls = OrderEnumerator.orderEntries(project).runtimeOnly().classes().usingCache().getPathsList().getPathList(); - final List urls = new ArrayList<>(); - - for (String path : compiledClassUrls) { - try { - urls.add(new File(FileUtil.toSystemIndependentName(path)).toURI().toURL()); - } catch (MalformedURLException e1) { - LOG.error(e1); + public static final String PLUGIN_ID = "org.antlr.intellij.plugin"; + + public static final Key EDITOR_MOUSE_LISTENER_KEY = Key.create("EDITOR_MOUSE_LISTENER_KEY"); + public static final Logger LOG = Logger.getInstance("ANTLRv4PluginController"); + + public static final String PREVIEW_WINDOW_ID = "ANTLR Preview"; + public static final String CONSOLE_WINDOW_ID = "Tool Output"; + + public boolean projectIsClosed = false; + + public Project project; + public ConsoleView console; + private ToolWindow consoleWindow; + + public Map grammarToPreviewState = + Collections.synchronizedMap(new HashMap<>()); + private ToolWindow previewWindow; // same for all grammar editor + public PreviewPanel previewPanel; // same for all grammar editor + + public MyVirtualFileAdapter myVirtualFileAdapter = new MyVirtualFileAdapter(); + public MyFileEditorManagerAdapter myFileEditorManagerAdapter = new MyFileEditorManagerAdapter(); + + private ProgressIndicator parsingProgressIndicator; + private UrlClassLoader projectClassLoader; + + private Class tokenStreamClass; + private Class charStreamClass; + + public ANTLRv4PluginController(Project project) { + this.project = project; + } + + public static ANTLRv4PluginController getInstance(Project project) { + if (project == null) { + LOG.error("getInstance: project is null"); + return null; + } + ANTLRv4PluginController pc = project.getComponent(ANTLRv4PluginController.class); + if (pc == null) { + LOG.error("getInstance: getComponent() for " + project.getName() + " returns null"); + } + return pc; + } + + @Override + public void initComponent() { + } + + @Override + public void projectOpened() { + IdeaPluginDescriptor plugin = PluginManager.getPlugin(PluginId.getId(PLUGIN_ID)); + String version = "unknown"; + if (plugin != null) { + version = plugin.getVersion(); + } + LOG.info("ANTLR 4 Plugin version " + version + ", Java version " + SystemInfo.JAVA_VERSION); + // make sure the tool windows are created early + createToolWindows(); + installListeners(); + initClassLoader(); + } + + public void createToolWindows() { + LOG.info("createToolWindows " + project.getName()); + ToolWindowManager toolWindowManager = ToolWindowManager.getInstance(project); + + previewPanel = new PreviewPanel(project); + + ContentFactory contentFactory = ContentFactory.SERVICE.getInstance(); + + toolWindowManager.invokeLater(() -> { + Content content = contentFactory.createContent(previewPanel, "", false); + content.setCloseable(false); + + previewWindow = toolWindowManager.registerToolWindow(PREVIEW_WINDOW_ID, true, ToolWindowAnchor.BOTTOM); + previewWindow.getContentManager().addContent(content); + previewWindow.setIcon(Icons.getToolWindow()); + }); + + TextConsoleBuilderFactory factory = TextConsoleBuilderFactory.getInstance(); + TextConsoleBuilder consoleBuilder = factory.createBuilder(project); + this.console = consoleBuilder.getConsole(); + + toolWindowManager.invokeLater(() -> { + JComponent consoleComponent = console.getComponent(); + Content content = contentFactory.createContent(consoleComponent, "", false); + content.setCloseable(false); + + consoleWindow = toolWindowManager.registerToolWindow(CONSOLE_WINDOW_ID, true, ToolWindowAnchor.BOTTOM); + consoleWindow.getContentManager().addContent(content); + consoleWindow.setIcon(Icons.getToolWindow()); + }); + } + + @Override + public void projectClosed() { + LOG.info("projectClosed " + project.getName()); + //synchronized ( shutdownLock ) { // They should be called from EDT only so no lock + projectIsClosed = true; + uninstallListeners(); + + console.dispose(); + + for (PreviewState it : grammarToPreviewState.values()) { + previewPanel.inputPanel.releaseEditor(it); + } + + previewPanel = null; + previewWindow = null; + consoleWindow = null; + project = null; + grammarToPreviewState = null; + } + + // seems that intellij can kill and reload a project w/o user knowing. + // a ptr was left around that pointed at a disposed project. led to + // problem in switchGrammar. Probably was a listener still attached and trigger + // editor listeners released in editorReleased() events. + public void uninstallListeners() { + VirtualFileManager.getInstance().removeVirtualFileListener(myVirtualFileAdapter); + + if (!project.isDisposed()) { + MessageBusConnection msgBus = project.getMessageBus().connect(project); + msgBus.disconnect(); + } + } + + @Override + public void disposeComponent() { + } + + @NotNull + @Override + public String getComponentName() { + return "antlr.ProjectComponent"; + } + + private void initClassLoader() { + List compiledClassUrls = OrderEnumerator.orderEntries(project).runtimeOnly().classes().usingCache().getPathsList().getPathList(); + final List urls = new ArrayList<>(); + + for (String path : compiledClassUrls) { + try { + urls.add(new File(FileUtil.toSystemIndependentName(path)).toURI().toURL()); + } catch (MalformedURLException e1) { + LOG.error(e1); + } + } + + this.projectClassLoader = UrlClassLoader.build().parent(TokenStream.class.getClassLoader()).urls(urls).get(); + + try { + this.tokenStreamClass = Class.forName(TokenStream.class.getName(), true, this.projectClassLoader); + this.charStreamClass = Class.forName(CharStream.class.getName(), true, this.projectClassLoader); + } catch (ClassNotFoundException e) { + LOG.error(e); + } + } + + // ------------------------------ + + public void installListeners() { + LOG.info("installListeners " + project.getName()); + // Listen for .g4 file saves + VirtualFileManager.getInstance().addVirtualFileListener(myVirtualFileAdapter); + + // Listen for editor window changes + MessageBusConnection msgBus = project.getMessageBus().connect(project); + msgBus.subscribe( + FileEditorManagerListener.FILE_EDITOR_MANAGER, + myFileEditorManagerAdapter + ); + + EditorFactory factory = EditorFactory.getInstance(); + factory.addEditorFactoryListener( + new EditorFactoryAdapter() { + @Override + public void editorCreated(@NotNull EditorFactoryEvent event) { + final Editor editor = event.getEditor(); + final Document doc = editor.getDocument(); + VirtualFile vfile = FileDocumentManager.getInstance().getFile(doc); + if (vfile != null && vfile.getName().endsWith(".g4")) { + GrammarEditorMouseAdapter listener = new GrammarEditorMouseAdapter(); + editor.putUserData(EDITOR_MOUSE_LISTENER_KEY, listener); + editor.addEditorMouseListener(listener); + } + } + + @Override + public void editorReleased(@NotNull EditorFactoryEvent event) { + Editor editor = event.getEditor(); + if (editor.getProject() != null && editor.getProject() != project) { + return; + } + GrammarEditorMouseAdapter listener = editor.getUserData(EDITOR_MOUSE_LISTENER_KEY); + if (listener != null) { + editor.removeEditorMouseListener(listener); + editor.putUserData(EDITOR_MOUSE_LISTENER_KEY, null); + } + } } - } - - this.projectClassLoader = UrlClassLoader.build().parent(TokenStream.class.getClassLoader()).urls(urls).get(); - - try { - this.tokenStreamClass = Class.forName(TokenStream.class.getName(), true, this.projectClassLoader); - this.charStreamClass = Class.forName(CharStream.class.getName(), true, this.projectClassLoader); - } catch (ClassNotFoundException e) { - LOG.error(e); - } - } - - // ------------------------------ - - public void installListeners() { - LOG.info("installListeners "+project.getName()); - // Listen for .g4 file saves - VirtualFileManager.getInstance().addVirtualFileListener(myVirtualFileAdapter); - - // Listen for editor window changes - MessageBusConnection msgBus = project.getMessageBus().connect(project); - msgBus.subscribe( - FileEditorManagerListener.FILE_EDITOR_MANAGER, - myFileEditorManagerAdapter - ); - - EditorFactory factory = EditorFactory.getInstance(); - factory.addEditorFactoryListener( - new EditorFactoryAdapter() { - @Override - public void editorCreated(@NotNull EditorFactoryEvent event) { - final Editor editor = event.getEditor(); - final Document doc = editor.getDocument(); - VirtualFile vfile = FileDocumentManager.getInstance().getFile(doc); - if ( vfile!=null && vfile.getName().endsWith(".g4") ) { - GrammarEditorMouseAdapter listener = new GrammarEditorMouseAdapter(); - editor.putUserData(EDITOR_MOUSE_LISTENER_KEY, listener); - editor.addEditorMouseListener(listener); - } - } - - @Override - public void editorReleased(@NotNull EditorFactoryEvent event) { - Editor editor = event.getEditor(); - if (editor.getProject() != null && editor.getProject() != project) { - return; - } - GrammarEditorMouseAdapter listener = editor.getUserData(EDITOR_MOUSE_LISTENER_KEY); - if (listener != null) { - editor.removeEditorMouseListener(listener); - editor.putUserData(EDITOR_MOUSE_LISTENER_KEY, null); - } - } - } - ); - } + ); + } /** The test ANTLR rule action triggers this event. This can occur * only occur when the current editor is showing a grammar, because @@ -298,16 +300,15 @@ public void setStartRuleNameEvent(VirtualFile grammarFile, String startRuleName) } } - public void grammarFileSavedEvent(VirtualFile grammarFile) { - LOG.info("grammarFileSavedEvent "+grammarFile.getPath()+" "+project.getName()); - updateGrammarObjectsFromFile(grammarFile, true); // force reload - if ( previewPanel!=null ) { - previewPanel.grammarFileSaved(grammarFile); - } - else { - LOG.error("grammarFileSavedEvent called before preview panel created"); - } - } + public void grammarFileSavedEvent(VirtualFile grammarFile) { + LOG.info("grammarFileSavedEvent " + grammarFile.getPath() + " " + project.getName()); + updateGrammarObjectsFromFile(grammarFile, true); // force reload + if (previewPanel != null) { + previewPanel.grammarFileSaved(grammarFile); + } else { + LOG.error("grammarFileSavedEvent called before preview panel created"); + } + } public void currentEditorFileChangedEvent(VirtualFile oldFile, VirtualFile newFile) { LOG.info("currentEditorFileChangedEvent "+(oldFile!=null?oldFile.getPath():"none")+ @@ -324,170 +325,172 @@ public void currentEditorFileChangedEvent(VirtualFile oldFile, VirtualFile newFi return; } - // When switching from a lexer grammar, update its objects in case the grammar was modified. - // The updated objects might be needed later by another dependant grammar. - if ( oldFile != null && oldFile.getName().endsWith(".g4")) { - updateGrammarObjectsFromFile(oldFile, true); - } - - PreviewState previewState = getPreviewState(newFile); - if ( previewState.g==null && previewState.lg==null ) { // only load grammars if none is there - updateGrammarObjectsFromFile(newFile, false); - } - if ( previewPanel!=null ) { - previewPanel.grammarFileChanged(newFile); - } - } - - public void mouseEnteredGrammarEditorEvent(VirtualFile vfile, EditorMouseEvent e) { - if ( previewPanel!=null ) { - ProfilerPanel profilerPanel = previewPanel.getProfilerPanel(); - if ( profilerPanel!=null ) { - profilerPanel.mouseEnteredGrammarEditorEvent(vfile, e); - } - } - } + // When switching from a lexer grammar, update its objects in case the grammar was modified. + // The updated objects might be needed later by another dependant grammar. + if (oldFile != null && oldFile.getName().endsWith(".g4")) { + updateGrammarObjectsFromFile(oldFile, true); + } + + PreviewState previewState = getPreviewState(newFile); + if (previewState.g == null && previewState.lg == null) { // only load grammars if none is there + updateGrammarObjectsFromFile(newFile, false); + } + if (previewPanel != null) { + previewPanel.grammarFileChanged(newFile); + } + } + + public void mouseEnteredGrammarEditorEvent(VirtualFile vfile, EditorMouseEvent e) { + if (previewPanel != null) { + ProfilerPanel profilerPanel = previewPanel.getProfilerPanel(); + if (profilerPanel != null) { + profilerPanel.mouseEnteredGrammarEditorEvent(vfile, e); + } + } + } + + public void editorFileClosedEvent(VirtualFile vfile) { + // hopefully called only from swing EDT + String grammarFileName = vfile.getPath(); + LOG.info("editorFileClosedEvent " + grammarFileName + " " + project.getName()); + if (!vfile.getName().endsWith(".g4")) { + hidePreview(); + return; + } + + // Dispose of state, editor, and such for this file + PreviewState previewState = grammarToPreviewState.get(grammarFileName); + if (previewState == null) { // project closing must have done already + return; + } + + previewState.g = null; // wack old ref to the Grammar for text in editor + previewState.lg = null; + + previewPanel.closeGrammar(vfile); + + grammarToPreviewState.remove(grammarFileName); + + // close tool window + hidePreview(); + } + + private void hidePreview() { + if (previewPanel != null) { + previewPanel.setEnabled(false); + } + if (previewWindow != null) { + previewWindow.hide(null); + } + } + + /** + * Make sure to run after updating grammars in previewState + */ + public void runANTLRTool(final VirtualFile grammarFile) { + String title = "ANTLR Code Generation"; + boolean canBeCancelled = true; + boolean forceGeneration = false; + Task gen = + new RunANTLROnGrammarFile(grammarFile, + project, + title, + canBeCancelled, + forceGeneration); + ProgressManager.getInstance().run(gen); + } + + /** + * Look for state information concerning this grammar file and update + * the Grammar objects. This does not necessarily update the grammar file + * in the current editor window. Either we are already looking at + * this grammar or we will have seen a grammar file changed event. + * (I hope!) + */ + private void updateGrammarObjectsFromFile(VirtualFile grammarFile, boolean generateTokensFile) { + updateGrammarObjectsFromFile_(grammarFile); + + // if grammarFileName is a separate lexer, we need to look for + // its matching parser, if any, that is loaded in an editor + // (don't go looking on disk). + PreviewState s = getAssociatedParserIfLexer(grammarFile.getPath()); + if (s != null) { + if (generateTokensFile) { + // Run the tool to regenerate the .tokens file, which will be + // needed in the parser grammar + runANTLRTool(grammarFile); + } + + // try to load lexer again and associate with this parser grammar. + // must update parser too as tokens have changed + updateGrammarObjectsFromFile_(s.grammarFile); + } + } + + private String updateGrammarObjectsFromFile_(VirtualFile grammarFile) { + String grammarFileName = grammarFile.getPath(); + PreviewState previewState = getPreviewState(grammarFile); + Grammar[] grammars = ParsingUtils.loadGrammars(grammarFile, project); + if (grammars != null) { + LexerGrammar lg = (LexerGrammar) grammars[0]; + Grammar g = grammars[1]; + + Constructor parserCtor = null; + Constructor lexerCtor = null; + + ANTLRv4GrammarProperties grammarProperties = ANTLRv4GrammarPropertiesStore.getGrammarProperties(project, grammarFile); + if (grammarProperties.isUseGeneratedParserCodeCheckBox()) { + String parserClassName = grammarProperties.getGeneratedParserClassName() != null ? grammarProperties.getGeneratedParserClassName() : grammarFile.getNameWithoutExtension(); + String lexerClassName = grammarProperties.getGeneratedLexerClassName() != null ? grammarProperties.getGeneratedLexerClassName() : lg.name; - public void editorFileClosedEvent(VirtualFile vfile) { - // hopefully called only from swing EDT - String grammarFileName = vfile.getPath(); - LOG.info("editorFileClosedEvent "+ grammarFileName+" "+project.getName()); - if ( !vfile.getName().endsWith(".g4") ) { - hidePreview(); - return; - } - - // Dispose of state, editor, and such for this file - PreviewState previewState = grammarToPreviewState.get(grammarFileName); - if ( previewState==null ) { // project closing must have done already - return; - } - - previewState.g = null; // wack old ref to the Grammar for text in editor - previewState.lg = null; - - previewPanel.closeGrammar(vfile); - - grammarToPreviewState.remove(grammarFileName); - - // close tool window - hidePreview(); - } - - private void hidePreview() { - if (previewPanel != null) { - previewPanel.setEnabled(false); - } - if (previewWindow != null) { - previewWindow.hide(null); - } - } - - /** Make sure to run after updating grammars in previewState */ - public void runANTLRTool(final VirtualFile grammarFile) { - String title = "ANTLR Code Generation"; - boolean canBeCancelled = true; - boolean forceGeneration = false; - Task gen = - new RunANTLROnGrammarFile(grammarFile, - project, - title, - canBeCancelled, - forceGeneration); - ProgressManager.getInstance().run(gen); - } - - /** Look for state information concerning this grammar file and update - * the Grammar objects. This does not necessarily update the grammar file - * in the current editor window. Either we are already looking at - * this grammar or we will have seen a grammar file changed event. - * (I hope!) - */ - private void updateGrammarObjectsFromFile(VirtualFile grammarFile, boolean generateTokensFile) { - updateGrammarObjectsFromFile_(grammarFile); - - // if grammarFileName is a separate lexer, we need to look for - // its matching parser, if any, that is loaded in an editor - // (don't go looking on disk). - PreviewState s = getAssociatedParserIfLexer(grammarFile.getPath()); - if ( s!=null ) { - if (generateTokensFile) { - // Run the tool to regenerate the .tokens file, which will be - // needed in the parser grammar - runANTLRTool(grammarFile); - } - - // try to load lexer again and associate with this parser grammar. - // must update parser too as tokens have changed - updateGrammarObjectsFromFile_(s.grammarFile); - } - } + try { + Class parserClass = (Class) Class.forName(parserClassName, true, this.projectClassLoader); + Class lexerClass = (Class) Class.forName(lexerClassName, true, this.projectClassLoader); - private String updateGrammarObjectsFromFile_(VirtualFile grammarFile) { - String grammarFileName = grammarFile.getPath(); - PreviewState previewState = getPreviewState(grammarFile); - Grammar[] grammars = ParsingUtils.loadGrammars(grammarFile, project); - if (grammars != null) { - LexerGrammar lg = (LexerGrammar) grammars[0]; - Grammar g = grammars[1]; - - Constructor parserCtor = null; - Constructor lexerCtor = null; - - ANTLRv4GrammarProperties grammarProperties = ANTLRv4GrammarPropertiesStore.getGrammarProperties(project, grammarFile); - if (grammarProperties.isUseGeneratedParserCodeCheckBox()) { - String parserClassName = grammarProperties.getGeneratedParserClassName() != null ? grammarProperties.getGeneratedParserClassName() : grammarFile.getNameWithoutExtension(); - String lexerClassName = grammarProperties.getGeneratedLexerClassName() != null ? grammarProperties.getGeneratedLexerClassName() : lg.name; - - try { - Class parserClass = (Class) Class.forName(parserClassName, true, this.projectClassLoader); - Class lexerClass = (Class) Class.forName(lexerClassName, true, this.projectClassLoader); - - parserCtor = parserClass.getDeclaredConstructor(tokenStreamClass); - lexerCtor = lexerClass.getDeclaredConstructor(charStreamClass); - } catch (ClassNotFoundException | NoSuchMethodException e) { - LOG.warn(e); - } + parserCtor = parserClass.getDeclaredConstructor(tokenStreamClass); + lexerCtor = lexerClass.getDeclaredConstructor(charStreamClass); + } catch (ClassNotFoundException | NoSuchMethodException e) { + LOG.warn(e); } + } + + synchronized (previewState) { + previewState.lg = lg; + previewState.g = g; + previewState.lexerCtor = lexerCtor; + previewState.parserCtor = parserCtor; + } + } + return grammarFileName; + } + + // TODO there could be multiple grammars importing/tokenVocab'ing this lexer grammar + public PreviewState getAssociatedParserIfLexer(String grammarFileName) { + for (PreviewState s : grammarToPreviewState.values()) { + if (s != null && s.lg != null && + (sameFile(grammarFileName, s.lg.fileName) || s.lg == ParsingUtils.BAD_LEXER_GRAMMAR)) { + // s has a lexer with same filename, see if there is a parser grammar + // (not a combined grammar) + if (s.g != null && s.g.getType() == ANTLRParser.PARSER) { + return s; + } + } - synchronized (previewState) { - previewState.lg = lg; - previewState.g = g; - previewState.lexerCtor = lexerCtor; - previewState.parserCtor = parserCtor; - } - } - return grammarFileName; - } - - // TODO there could be multiple grammars importing/tokenVocab'ing this lexer grammar - public PreviewState getAssociatedParserIfLexer(String grammarFileName) { - for (PreviewState s : grammarToPreviewState.values()) { - if ( s!=null && s.lg!=null && - (sameFile(grammarFileName, s.lg.fileName)||s.lg==ParsingUtils.BAD_LEXER_GRAMMAR) ) - { - // s has a lexer with same filename, see if there is a parser grammar - // (not a combined grammar) - if ( s.g!=null && s.g.getType()==ANTLRParser.PARSER ) { - return s; - } - } - - if ( s!=null && s.g!=null && s.g.importedGrammars!=null ) { - for ( Grammar importedGrammar : s.g.importedGrammars ) { - if (grammarFileName.equals(importedGrammar.fileName)) { - return s; - } - } - } - } - return null; - } + if (s != null && s.g != null && s.g.importedGrammars != null) { + for (Grammar importedGrammar : s.g.importedGrammars) { + if (grammarFileName.equals(importedGrammar.fileName)) { + return s; + } + } + } + } + return null; + } - private boolean sameFile(String pathOne, String pathTwo) { - // use new File() to support both / and \ in paths - return new File(pathOne).equals(new File(pathTwo)); - } + private boolean sameFile(String pathOne, String pathTwo) { + // use new File() to support both / and \ in paths + return new File(pathOne).equals(new File(pathTwo)); + } public void parseText(final VirtualFile grammarFile, String inputText) { final PreviewState previewState = getPreviewState(grammarFile); @@ -499,10 +502,10 @@ public void parseText(final VirtualFile grammarFile, String inputText) { // System.out.println("PARSE START "+Thread.currentThread().getName()); long start = System.nanoTime(); - previewState.parsingResult = ParsingUtils.parseText( - previewState.g, previewState.lg, previewState.lexerCtor, previewState.parserCtor, - previewState.startRuleName, grammarFile, inputText, project - ); + previewState.parsingResult = ParsingUtils.parseText( + previewState.g, previewState.lg, previewState.lexerCtor, previewState.parserCtor, + previewState.startRuleName, grammarFile, inputText, project + ); // long parseTime_ns = System.nanoTime() - start; // double parseTimeMS = parseTime_ns/(1000.0*1000.0); @@ -515,13 +518,13 @@ public void parseText(final VirtualFile grammarFile, String inputText) { ); } - public void abortCurrentParsing() { - if ( parsingProgressIndicator!=null ) { - parsingProgressIndicator.cancel(); - parsingProgressIndicator = null; - previewPanel.onParsingCancelled(); - } - } + public void abortCurrentParsing() { + if (parsingProgressIndicator != null) { + parsingProgressIndicator.cancel(); + parsingProgressIndicator = null; + previewPanel.onParsingCancelled(); + } + } public void startParsing() { parsingProgressIndicator = null; @@ -533,113 +536,113 @@ public PreviewPanel getPreviewPanel() { return previewPanel; } - public ConsoleView getConsole() { - return console; - } - - public ToolWindow getConsoleWindow() { - return consoleWindow; - } - - public static void showConsoleWindow(final Project project) { - ApplicationManager.getApplication().invokeLater( - () -> ANTLRv4PluginController.getInstance(project).getConsoleWindow().show(null) - ); - } - - public ToolWindow getPreviewWindow() { - return previewWindow; - } - - public @NotNull PreviewState getPreviewState(VirtualFile grammarFile) { - // make sure only one thread tries to add a preview state object for a given file - String grammarFileName = grammarFile.getPath(); - // Have we seen this grammar before? - PreviewState stateForCurrentGrammar = grammarToPreviewState.get(grammarFileName); - if ( stateForCurrentGrammar!=null ) { - return stateForCurrentGrammar; // seen this before - } - - // not seen, must create state - stateForCurrentGrammar = new PreviewState(project, grammarFile); - grammarToPreviewState.put(grammarFileName, stateForCurrentGrammar); - - return stateForCurrentGrammar; - } - - public Editor getEditor(VirtualFile vfile) { - final FileDocumentManager fdm = FileDocumentManager.getInstance(); - final Document doc = fdm.getDocument(vfile); - if (doc == null) return null; - - EditorFactory factory = EditorFactory.getInstance(); - final Editor[] editors = factory.getEditors(doc, previewPanel.project); - if ( editors.length==0 ) { - // no editor found for this file. likely an out-of-sequence issue - // where Intellij is opening a project and doesn't fire events - // in order we'd expect. - return null; - } - return editors[0]; // hope just one - } - - - /** Get the state information associated with the grammar in the current - * editor window. If there is no grammar in the editor window, return null. - * If there is a grammar, return any existing preview state else - * create a new one in store in the map. - * - * Too dangerous; turning off but might be useful later. - public @org.jetbrains.annotations.Nullable PreviewState getPreviewState() { - VirtualFile currentGrammarFile = getCurrentGrammarFile(); - if ( currentGrammarFile==null ) { - return null; - } - String currentGrammarFileName = currentGrammarFile.getPath(); - if ( currentGrammarFileName==null ) { - return null; // we are not looking at a grammar file - } - return getPreviewState(currentGrammarFile); - } - */ - - // These "get current editor file" routines should only be used - // when you are sure the user is in control and is viewing the - // right file (i.e., don't use these during project loading etc...) - - public static VirtualFile getCurrentEditorFile(Project project) { - FileEditorManager fmgr = FileEditorManager.getInstance(project); - // "If more than one file is selected (split), the file with most recent focused editor is returned first." from IDE doc on method - VirtualFile[] files = fmgr.getSelectedFiles(); - if ( files.length == 0 ) { - return null; - } - return files[0]; - } - - public VirtualFile getCurrentGrammarFile() { - return getCurrentGrammarFile(project); - } - - public static VirtualFile getCurrentGrammarFile(Project project) { - VirtualFile f = getCurrentEditorFile(project); - if ( f==null ) { - return null; - } - if ( f.getName().endsWith(".g4") ) return f; - return null; - } - - private class GrammarEditorMouseAdapter extends EditorMouseAdapter { - @Override - public void mouseClicked(EditorMouseEvent e) { - Document doc = e.getEditor().getDocument(); - VirtualFile vfile = FileDocumentManager.getInstance().getFile(doc); - if ( vfile!=null && vfile.getName().endsWith(".g4") ) { - mouseEnteredGrammarEditorEvent(vfile, e); - } - } - } + public ConsoleView getConsole() { + return console; + } + + public ToolWindow getConsoleWindow() { + return consoleWindow; + } + + public static void showConsoleWindow(final Project project) { + ApplicationManager.getApplication().invokeLater( + () -> ANTLRv4PluginController.getInstance(project).getConsoleWindow().show(null) + ); + } + + public ToolWindow getPreviewWindow() { + return previewWindow; + } + + public @NotNull PreviewState getPreviewState(VirtualFile grammarFile) { + // make sure only one thread tries to add a preview state object for a given file + String grammarFileName = grammarFile.getPath(); + // Have we seen this grammar before? + PreviewState stateForCurrentGrammar = grammarToPreviewState.get(grammarFileName); + if (stateForCurrentGrammar != null) { + return stateForCurrentGrammar; // seen this before + } + + // not seen, must create state + stateForCurrentGrammar = new PreviewState(project, grammarFile); + grammarToPreviewState.put(grammarFileName, stateForCurrentGrammar); + + return stateForCurrentGrammar; + } + + public Editor getEditor(VirtualFile vfile) { + final FileDocumentManager fdm = FileDocumentManager.getInstance(); + final Document doc = fdm.getDocument(vfile); + if (doc == null) return null; + + EditorFactory factory = EditorFactory.getInstance(); + final Editor[] editors = factory.getEditors(doc, previewPanel.project); + if (editors.length == 0) { + // no editor found for this file. likely an out-of-sequence issue + // where Intellij is opening a project and doesn't fire events + // in order we'd expect. + return null; + } + return editors[0]; // hope just one + } + + + /** + * Get the state information associated with the grammar in the current + * editor window. If there is no grammar in the editor window, return null. + * If there is a grammar, return any existing preview state else + * create a new one in store in the map. + *

+ * Too dangerous; turning off but might be useful later. + * public @org.jetbrains.annotations.Nullable PreviewState getPreviewState() { + * VirtualFile currentGrammarFile = getCurrentGrammarFile(); + * if ( currentGrammarFile==null ) { + * return null; + * } + * String currentGrammarFileName = currentGrammarFile.getPath(); + * if ( currentGrammarFileName==null ) { + * return null; // we are not looking at a grammar file + * } + * return getPreviewState(currentGrammarFile); + * } + */ + + // These "get current editor file" routines should only be used + // when you are sure the user is in control and is viewing the + // right file (i.e., don't use these during project loading etc...) + public static VirtualFile getCurrentEditorFile(Project project) { + FileEditorManager fmgr = FileEditorManager.getInstance(project); + // "If more than one file is selected (split), the file with most recent focused editor is returned first." from IDE doc on method + VirtualFile[] files = fmgr.getSelectedFiles(); + if (files.length == 0) { + return null; + } + return files[0]; + } + + public VirtualFile getCurrentGrammarFile() { + return getCurrentGrammarFile(project); + } + + public static VirtualFile getCurrentGrammarFile(Project project) { + VirtualFile f = getCurrentEditorFile(project); + if (f == null) { + return null; + } + if (f.getName().endsWith(".g4")) return f; + return null; + } + + private class GrammarEditorMouseAdapter extends EditorMouseAdapter { + @Override + public void mouseClicked(EditorMouseEvent e) { + Document doc = e.getEditor().getDocument(); + VirtualFile vfile = FileDocumentManager.getInstance().getFile(doc); + if (vfile != null && vfile.getName().endsWith(".g4")) { + mouseEnteredGrammarEditorEvent(vfile, e); + } + } + } private class MyVirtualFileAdapter extends VirtualFileAdapter { @Override diff --git a/src/main/java/org/antlr/intellij/plugin/parsing/ParsingUtils.java b/src/main/java/org/antlr/intellij/plugin/parsing/ParsingUtils.java index ddfcd082..81b3dccf 100644 --- a/src/main/java/org/antlr/intellij/plugin/parsing/ParsingUtils.java +++ b/src/main/java/org/antlr/intellij/plugin/parsing/ParsingUtils.java @@ -1,21 +1,13 @@ package org.antlr.intellij.plugin.parsing; -import com.intellij.CommonBundle; import com.intellij.execution.ui.ConsoleView; import com.intellij.execution.ui.ConsoleViewContentType; -import com.intellij.openapi.compiler.CompilerManager; import com.intellij.openapi.editor.Document; import com.intellij.openapi.fileEditor.FileDocumentManager; import com.intellij.openapi.project.Project; -import com.intellij.openapi.project.ProjectManager; -import com.intellij.openapi.project.ProjectUtil; -import com.intellij.openapi.roots.OrderEnumerator; -import com.intellij.openapi.ui.Messages; -import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.vfs.LocalFileSystem; import com.intellij.openapi.vfs.VfsUtil; import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.util.lang.UrlClassLoader; import org.antlr.intellij.adaptor.parser.SyntaxErrorListener; import org.antlr.intellij.plugin.ANTLRv4PluginController; import org.antlr.intellij.plugin.PluginIgnoreMissingTokensFileErrorManager; @@ -37,9 +29,7 @@ import org.antlr.v4.tool.ErrorType; import org.antlr.v4.tool.Grammar; import org.antlr.v4.tool.LexerGrammar; -import org.antlr.v4.tool.Rule; import org.antlr.v4.tool.ast.GrammarRootAST; -import org.codehaus.groovy.antlr.AntlrParserPlugin; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -47,8 +37,6 @@ import java.io.IOException; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; -import java.net.MalformedURLException; -import java.net.URL; import java.util.*; import static org.antlr.intellij.plugin.configdialogs.ANTLRv4GrammarPropertiesStore.getGrammarProperties; From 3f2b30150e3ce7cb9648a756b91f2381b240b908 Mon Sep 17 00:00:00 2001 From: Adam Kovari Date: Mon, 8 Aug 2022 16:08:00 +0200 Subject: [PATCH 7/7] 1.19.2-akovari --- gradle.properties | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gradle.properties b/gradle.properties index 00f10512..7b955ffd 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ -pluginVersion=1.19.2 +pluginVersion=1.19.2-akovari # e.g. IC-2016.3.3, IU-2018.2.5 etc # For a list of possible values, refer to the section 'com.jetbrains.intellij.idea' at @@ -16,7 +16,7 @@ pluginVersion=1.19.2 #ideaVersion=IC-2021.2 -ideaVersion=IC-2021.3.3 +ideaVersion=IC-2022.2 # The version of ANTLR v4 that will be used to generate the parser antlr4Version=4.10.1