diff --git a/README.md b/README.md
index f78ec640..0c63cdd8 100644
--- a/README.md
+++ b/README.md
@@ -64,6 +64,12 @@ 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. 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
See [Releases](https://github.com/antlr/intellij-plugin-v4/releases)
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/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
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..5d5dc8b1 100644
--- a/src/main/java/org/antlr/intellij/plugin/ANTLRv4PluginController.java
+++ b/src/main/java/org/antlr/intellij/plugin/ANTLRv4PluginController.java
@@ -26,8 +26,10 @@
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.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,212 +39,248 @@
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;
-
-/** 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.
+import java.lang.reflect.Constructor;
+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,
+ * 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;
-
- 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();
- }
-
- 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";
- }
-
- // ------------------------------
-
- 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);
- }
- }
- }
- );
- }
+ 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);
+ }
+ }
+ }
+ );
+ }
/** The test ANTLR rule action triggers this event. This can occur
* only occur when the current editor is showing a grammar, because
@@ -262,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")+
@@ -288,152 +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);
- }
- }
- }
-
- 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) {
- 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;
- }
-
- // 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;
- }
-
- private boolean sameFile(String pathOne, String pathTwo) {
- // use new File() to support both / and \ in paths
- return new File(pathOne).equals(new File(pathTwo));
- }
+ // 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;
+
+ 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);
+ }
+ }
+
+ 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;
+ }
+
+ 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);
@@ -445,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.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);
@@ -461,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;
@@ -479,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/configdialogs/ANTLRv4GrammarProperties.java b/src/main/java/org/antlr/intellij/plugin/configdialogs/ANTLRv4GrammarProperties.java
index 879411d7..f89cac57 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,15 @@ public class ANTLRv4GrammarProperties implements Cloneable {
@OptionTag(converter = CaseChangingStrategyConverter.class)
CaseChangingStrategy caseChangingStrategy = CaseChangingStrategy.LEAVE_AS_IS;
+ @Property
+ boolean useGeneratedParserCodeCheckBox = false;
+
+ @Property
+ String generatedParserClassName;
+
+ @Property
+ String generatedLexerClassName;
+
public ANTLRv4GrammarProperties() {
}
@@ -92,6 +101,9 @@ public ANTLRv4GrammarProperties(ANTLRv4GrammarProperties source) {
this.generateListener = source.generateListener;
this.generateVisitor = source.generateVisitor;
this.caseChangingStrategy = source.caseChangingStrategy;
+ this.useGeneratedParserCodeCheckBox = source.useGeneratedParserCodeCheckBox;
+ this.generatedParserClassName = source.generatedParserClassName;
+ this.generatedLexerClassName = source.generatedLexerClassName;
}
public boolean shouldAutoGenerateParser() {
@@ -126,6 +138,18 @@ public boolean shouldGenerateParseTreeVisitor() {
return generateVisitor;
}
+ 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 138c607b..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 @@
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..ffe65ef8 100644
--- a/src/main/java/org/antlr/intellij/plugin/configdialogs/ConfigANTLRPerGrammar.java
+++ b/src/main/java/org/antlr/intellij/plugin/configdialogs/ConfigANTLRPerGrammar.java
@@ -32,6 +32,9 @@ public class ConfigANTLRPerGrammar extends DialogWrapper {
protected JCheckBox autoGenerateParsersCheckBox;
protected JTextField languageField;
private JComboBox caseTransformation;
+ private JCheckBox useGeneratedParserCodeCheckBox;
+ private JTextField generatedParserClassName;
+ private JTextField generatedLexerClassName;
private ConfigANTLRPerGrammar(final Project project) {
super(project, false);
@@ -65,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) {
@@ -79,6 +91,9 @@ public void loadValues(Project project, String qualFileName) {
caseTransformation.setSelectedItem(grammarProperties.getCaseChangingStrategy());
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) {
@@ -93,6 +108,9 @@ public void saveValues(Project project, String qualFileName) {
grammarProperties.caseChangingStrategy = getCaseChangingStrategy();
grammarProperties.generateListener = generateParseTreeListenerCheckBox.isSelected();
grammarProperties.generateVisitor = generateParseTreeVisitorCheckBox.isSelected();
+ grammarProperties.useGeneratedParserCodeCheckBox = useGeneratedParserCodeCheckBox.isSelected();
+ grammarProperties.generatedParserClassName = getGeneratedParserClassName();
+ grammarProperties.generatedLexerClassName = getGeneratedLexerClassName();
}
boolean isModified(ANTLRv4GrammarProperties originalProperties) {
@@ -101,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() {
@@ -124,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();
}
@@ -139,9 +172,12 @@ public String toString() {
return "ConfigANTLRPerGrammar{" +
" generateParseTreeListenerCheckBox=" + generateParseTreeListenerCheckBox +
", generateParseTreeVisitorCheckBox=" + generateParseTreeVisitorCheckBox +
+ ", useGeneratedParserCodeCheckBox=" + useGeneratedParserCodeCheckBox +
", packageField=" + packageField +
", outputDirField=" + outputDirField +
", libDirField=" + libDirField +
+ ", generatedParserClassName=" + generatedParserClassName +
+ ", generatedLexerClassName=" + generatedLexerClassName +
'}';
}
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..81b3dccf 100644
--- a/src/main/java/org/antlr/intellij/plugin/parsing/ParsingUtils.java
+++ b/src/main/java/org/antlr/intellij/plugin/parsing/ParsingUtils.java
@@ -29,13 +29,14 @@
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.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.util.*;
import static org.antlr.intellij.plugin.configdialogs.ANTLRv4GrammarPropertiesStore.getGrammarProperties;
@@ -193,64 +194,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