Implementation of Goto Declaration and Completion using Indexer
diff --git a/java/languages.antlr/nbproject/project.xml b/java/languages.antlr/nbproject/project.xml
index 7de043e..22c1f1e 100644
--- a/java/languages.antlr/nbproject/project.xml
+++ b/java/languages.antlr/nbproject/project.xml
@@ -123,6 +123,23 @@
                     </run-dependency>
                 </dependency>
                 <dependency>
+                    <code-name-base>org.netbeans.modules.parsing.indexing</code-name-base>
+                    <build-prerequisite/>
+                    <compile-dependency/>
+                    <run-dependency>
+                        <specification-version>9.27</specification-version>
+                    </run-dependency>
+                </dependency>
+                <dependency>
+                    <code-name-base>org.netbeans.modules.projectapi</code-name-base>
+                    <build-prerequisite/>
+                    <compile-dependency/>
+                    <run-dependency>
+                        <release-version>1</release-version>
+                        <specification-version>1.89</specification-version>
+                    </run-dependency>
+                </dependency>
+                <dependency>
                     <code-name-base>org.openide.awt</code-name-base>
                     <build-prerequisite/>
                     <compile-dependency/>
diff --git a/java/languages.antlr/src/org/netbeans/modules/languages/antlr/AntlrCompletionProvider.java b/java/languages.antlr/src/org/netbeans/modules/languages/antlr/AntlrCompletionProvider.java
index 32986af..97e199e 100644
--- a/java/languages.antlr/src/org/netbeans/modules/languages/antlr/AntlrCompletionProvider.java
+++ b/java/languages.antlr/src/org/netbeans/modules/languages/antlr/AntlrCompletionProvider.java
@@ -19,8 +19,10 @@
 package org.netbeans.modules.languages.antlr;
 
 import java.util.Collections;
-import java.util.Map;
+import java.util.List;
+import java.util.Locale;
 import java.util.prefs.Preferences;
+import java.util.stream.Collectors;
 import javax.swing.text.Document;
 import javax.swing.text.JTextComponent;
 import org.netbeans.api.editor.mimelookup.MimeLookup;
@@ -39,6 +41,9 @@
 import org.netbeans.modules.parsing.api.Source;
 import org.netbeans.modules.parsing.api.UserTask;
 import org.netbeans.modules.parsing.spi.ParseException;
+import org.netbeans.modules.parsing.spi.indexing.support.IndexResult;
+import org.netbeans.modules.parsing.spi.indexing.support.QuerySupport;
+import org.netbeans.modules.parsing.spi.indexing.support.QuerySupport.Kind;
 import org.netbeans.spi.editor.completion.CompletionItem;
 import org.netbeans.spi.editor.completion.CompletionProvider;
 import org.netbeans.spi.editor.completion.CompletionResultSet;
@@ -46,8 +51,13 @@
 import org.netbeans.spi.editor.completion.support.AsyncCompletionQuery;
 import org.netbeans.spi.editor.completion.support.AsyncCompletionTask;
 import org.netbeans.spi.editor.completion.support.CompletionUtilities;
+import org.openide.filesystems.FileObject;
 import org.openide.util.Exceptions;
 
+import static org.netbeans.modules.languages.antlr.AntlrIndexer.FIELD_CASE_INSENSITIVE_DECLARATION;
+import static org.netbeans.modules.languages.antlr.AntlrIndexer.FIELD_DECLARATION;
+import static org.netbeans.modules.languages.antlr.AntlrIndexer.transitiveImports;
+
 /**
  *
  * @author Laszlo Kishalmi
@@ -103,19 +113,45 @@
                         @Override
                         public void run(ResultIterator resultIterator) throws Exception {
                             AntlrParserResult result = (AntlrParserResult) resultIterator.getParserResult(caretOffset);
-                            boolean isCaseSensitive = isCaseSensitive();
                             String prefix = getPrefix(result, caretOffset, true);
-                            if (prefix != null) {
-                                String mprefix = isCaseSensitive ? prefix : prefix.toUpperCase();
-                                Map<String, AntlrParserResult.Reference> refs = result.references;
-                                for (String ref : refs.keySet()) {
-                                    String mref = isCaseSensitive ? ref : ref.toUpperCase();
-                                    boolean match = mref.startsWith(mprefix);
-                                    if (match) {
-                                        CompletionItem item = CompletionUtilities.newCompletionItemBuilder(ref)
+                            FileObject fo = source.getFileObject();
+                            if (prefix != null && fo != null) {
+                                String prefix_ci = prefix.toLowerCase(Locale.ENGLISH);
+                                QuerySupport qs = AntlrIndexer.getQuerySupport(fo);
+                                QuerySupport.Query.Factory qf = qs.getQueryFactory();
+                                List<FileObject> candidates = transitiveImports(qs, fo);
+                                QuerySupport.Query query = qf.and(
+                                        // Only consider the file itself or imported files
+                                        qf.or(
+                                                candidates.stream()
+                                                        .map(fo2 -> qs.getQueryFactory().file(fo2))
+                                                        .collect(Collectors.toList())
+                                                        .toArray(new QuerySupport.Query[0])
+                                        ),
+                                        qf.field(
+                                                isCaseSensitive() ? FIELD_DECLARATION : FIELD_CASE_INSENSITIVE_DECLARATION,
+                                                isCaseSensitive() ? prefix : prefix_ci,
+                                                isCaseSensitive() ? Kind.PREFIX : Kind.CASE_INSENSITIVE_PREFIX
+                                        )
+                                );
+
+                                for (IndexResult ir : query.execute(FIELD_DECLARATION)) {
+                                    for (String value : ir.getValues(FIELD_DECLARATION)) {
+                                        if (isCaseSensitive()) {
+                                            if (!value.startsWith(prefix)) {
+                                                continue;
+                                            }
+                                        } else {
+                                            if(! value.toLowerCase(Locale.ENGLISH).startsWith(prefix_ci)) {
+                                                continue;
+                                            }
+                                        }
+                                        String[] values = value.split("\\\\");
+                                        CompletionItem item = CompletionUtilities
+                                                .newCompletionItemBuilder(values[0])
                                                 .startOffset(caretOffset - prefix.length())
-                                                .leftHtmlText(ref)
-                                                .sortText(ref)
+                                                .leftHtmlText(values[0])
+                                                .sortText(values[0])
                                                 .build();
                                         resultSet.addItem(item);
                                     }
diff --git a/java/languages.antlr/src/org/netbeans/modules/languages/antlr/AntlrDeclarationFinder.java b/java/languages.antlr/src/org/netbeans/modules/languages/antlr/AntlrDeclarationFinder.java
index 0ffc09f..a142964 100644
--- a/java/languages.antlr/src/org/netbeans/modules/languages/antlr/AntlrDeclarationFinder.java
+++ b/java/languages.antlr/src/org/netbeans/modules/languages/antlr/AntlrDeclarationFinder.java
@@ -18,15 +18,28 @@
  */
 package org.netbeans.modules.languages.antlr;
 
-import java.util.Map;
+import java.io.IOException;
+import java.util.List;
+import java.util.stream.Collectors;
 import javax.swing.text.AbstractDocument;
 import javax.swing.text.Document;
 import org.netbeans.api.lexer.Token;
 import org.netbeans.api.lexer.TokenHierarchy;
 import org.netbeans.api.lexer.TokenSequence;
 import org.netbeans.modules.csl.api.DeclarationFinder;
+import org.netbeans.modules.csl.api.ElementHandle;
+import org.netbeans.modules.csl.api.HtmlFormatter;
 import org.netbeans.modules.csl.api.OffsetRange;
 import org.netbeans.modules.csl.spi.ParserResult;
+import org.netbeans.modules.parsing.spi.indexing.support.IndexResult;
+import org.netbeans.modules.parsing.spi.indexing.support.QuerySupport;
+import org.netbeans.modules.parsing.spi.indexing.support.QuerySupport.Query;
+import org.netbeans.modules.parsing.spi.indexing.support.QuerySupport.Query.Factory;
+import org.openide.filesystems.FileObject;
+import org.openide.util.Exceptions;
+
+import static org.netbeans.modules.languages.antlr.AntlrIndexer.FIELD_DECLARATION;
+import static org.netbeans.modules.languages.antlr.AntlrIndexer.transitiveImports;
 
 /**
  *
@@ -36,16 +49,13 @@
 
     @Override
     public DeclarationLocation findDeclaration(ParserResult info, int caretOffset) {
-        AntlrParserResult result = (AntlrParserResult) info;
         TokenSequence<?> ts = info.getSnapshot().getTokenHierarchy().tokenSequence();
         ts.move(caretOffset);
         ts.movePrevious();
         ts.moveNext();
         Token<?> token = ts.token();
         String ref = String.valueOf(token.text());
-        Map<String, AntlrParserResult.Reference> refs = result.references;
-        AntlrParserResult.Reference aref = refs.get(ref);
-        return aref != null ? new DeclarationLocation(aref.source, aref.defOffset.getStart()) : DeclarationLocation.NONE;
+        return getDeclarationLocation(info.getSnapshot().getSource().getFileObject(), ref);
     }
 
     @Override
@@ -72,4 +82,89 @@
         }
     }
 
+    public static DeclarationLocation getDeclarationLocation(FileObject sourceFile, String name) {
+        try {
+            QuerySupport qs = AntlrIndexer.getQuerySupport(sourceFile);
+            Factory qf = qs.getQueryFactory();
+            String targetDefinition = name + AntlrIndexer.SEPARATOR;
+            List<FileObject> candidates = transitiveImports(qs, sourceFile);
+            Query query = qf.and(
+                    // Only consider the file itself or imported files
+                    qf.or(
+                           candidates.stream()
+                                    .map(fo -> qs.getQueryFactory().file(fo))
+                                    .collect(Collectors.toList())
+                                    .toArray(new Query[0])
+                    ),
+                    qf.field(FIELD_DECLARATION, targetDefinition, QuerySupport.Kind.PREFIX)
+            );
+
+            DeclarationFinder.DeclarationLocation dl = null;
+            for(IndexResult ir: query.execute(FIELD_DECLARATION)) {
+                for (String value : ir.getValues(FIELD_DECLARATION)) {
+                    if (!value.startsWith(targetDefinition)) {
+                        continue;
+                    }
+                    String[] values = value.split("\\\\");
+                    int start = Integer.parseInt(values[1]);
+                    int end = Integer.parseInt(values[2]);
+                    AntlrStructureItem asi = new AntlrStructureItem.RuleStructureItem(name, ir.getFile(), start, end);
+                    DeclarationLocation dln = new DeclarationFinder.DeclarationLocation(ir.getFile(), start, asi);
+                    if (dl == null) {
+                        dl = dln;
+                    }
+                    // If multiple declaration locations are possible (antlr4
+                    // allows redefinition), the original location must be part
+                    // of the alternative locations.
+                    //
+                    // The sortIdx describes the "depth" of the inheritence tree
+                    // until the location is found. Nearer imports are preferred
+                    dl.addAlternative(new AlternativeLocationImpl(dln, candidates.indexOf(ir.getFile())));
+                }
+            }
+            if(dl == null) {
+                dl = DeclarationFinder.DeclarationLocation.NONE;
+            }
+            return dl;
+        } catch (IOException ex) {
+            Exceptions.printStackTrace(ex);
+            return DeclarationFinder.DeclarationLocation.NONE;
+        }
+    }
+
+    private static class AlternativeLocationImpl implements AlternativeLocation {
+
+        private final DeclarationLocation location;
+        private final int sortIdx;
+
+        public AlternativeLocationImpl(DeclarationLocation location, int sortIdx) {
+            this.location = location;
+            this.sortIdx = sortIdx;
+        }
+
+        @Override
+        public ElementHandle getElement() {
+            return getLocation().getElement();
+        }
+
+        @Override
+        public String getDisplayHtml(HtmlFormatter formatter) {
+            return getLocation().toString();
+        }
+
+        @Override
+        public DeclarationFinder.DeclarationLocation getLocation() {
+            return location;
+        }
+
+        @Override
+        public int compareTo(DeclarationFinder.AlternativeLocation o) {
+            if(o instanceof AlternativeLocationImpl) {
+                return sortIdx - ((AlternativeLocationImpl) o).sortIdx;
+            } else {
+                return 0;
+            }
+        }
+
+    }
 }
diff --git a/java/languages.antlr/src/org/netbeans/modules/languages/antlr/AntlrIndexer.java b/java/languages.antlr/src/org/netbeans/modules/languages/antlr/AntlrIndexer.java
new file mode 100644
index 0000000..bd87004
--- /dev/null
+++ b/java/languages.antlr/src/org/netbeans/modules/languages/antlr/AntlrIndexer.java
@@ -0,0 +1,245 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.netbeans.modules.languages.antlr;
+
+import java.io.IOException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Set;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import org.netbeans.api.editor.mimelookup.MimeRegistration;
+import org.netbeans.api.editor.mimelookup.MimeRegistrations;
+import org.netbeans.modules.languages.antlr.v3.Antlr3Language;
+import org.netbeans.modules.languages.antlr.v4.Antlr4Language;
+import org.netbeans.modules.languages.antlr.v4.Antlr4ParserResult;
+import org.netbeans.modules.parsing.api.Snapshot;
+import org.netbeans.modules.parsing.spi.Parser;
+import org.netbeans.modules.parsing.spi.indexing.Context;
+import org.netbeans.modules.parsing.spi.indexing.EmbeddingIndexer;
+import org.netbeans.modules.parsing.spi.indexing.EmbeddingIndexerFactory;
+import org.netbeans.modules.parsing.spi.indexing.Indexable;
+import org.netbeans.modules.parsing.spi.indexing.support.IndexDocument;
+import org.netbeans.modules.parsing.spi.indexing.support.IndexingSupport;
+import org.netbeans.modules.parsing.spi.indexing.support.QuerySupport;
+import org.openide.filesystems.FileObject;
+import org.openide.util.Exceptions;
+
+public class AntlrIndexer extends EmbeddingIndexer {
+
+    // Used to split
+    public static final String SEPARATOR = "\\";
+    public static final String FIELD_IMPORT = "import";
+    public static final String FIELD_DECLARATION = "declaration";
+    public static final String FIELD_CASE_INSENSITIVE_DECLARATION = "ci-declaration";
+    public static final String FIELD_OCCURRENCE = "occurrence";
+    public static final String FIELD_CASE_INSENSITIVE_OCCURRENCE = "ci-occurrence";
+
+    private static final Logger LOG = Logger.getLogger(AntlrIndexer.class.getName());
+
+    @Override
+    protected void index(Indexable indexable, Parser.Result parserResult, Context context) {
+        IndexingSupport support;
+        try {
+            support = IndexingSupport.getInstance(context);
+        } catch (IOException ex) {
+            LOG.log(Level.WARNING, null, ex);
+            return;
+        }
+
+        IndexDocument id = support.createDocument(indexable);
+
+        if (parserResult instanceof AntlrParserResult) {
+            Map<String, AntlrParserResult.Reference> refs = ((AntlrParserResult) parserResult).references;
+            refs.values().stream().forEach(r -> {
+                String declarationValue =  r.name + SEPARATOR + r.defOffset.getStart() + SEPARATOR + r.defOffset.getEnd();
+                id.addPair(FIELD_DECLARATION, declarationValue, true, true);
+                id.addPair(FIELD_CASE_INSENSITIVE_DECLARATION, ci(declarationValue), true, true);
+                if(r.occurances != null) {
+                    r.occurances.stream().forEach(c2 -> {
+                        String occurrenceValue = r.name + SEPARATOR + c2.getStart() + SEPARATOR + c2.getEnd();
+                        id.addPair(FIELD_OCCURRENCE, occurrenceValue, true, true);
+                        id.addPair(FIELD_CASE_INSENSITIVE_OCCURRENCE, ci(declarationValue), true, true);
+                    });
+                }
+            });
+        }
+
+        if (parserResult instanceof Antlr4ParserResult) {
+            ((Antlr4ParserResult) parserResult).getImports()
+                    .forEach(s -> {
+                        id.addPair(FIELD_IMPORT, s, true, true);
+                    });
+        }
+
+        support.addDocument(id);
+    }
+
+    @MimeRegistrations({
+        @MimeRegistration(
+                mimeType = Antlr3Language.MIME_TYPE,
+                service =  EmbeddingIndexerFactory.class
+        ),
+        @MimeRegistration(
+                mimeType = Antlr4Language.MIME_TYPE,
+                service =  EmbeddingIndexerFactory.class
+        )
+    })
+    public static final class Factory extends EmbeddingIndexerFactory {
+
+        public static final String NAME = "antlr"; // NOI18N
+        public static final int VERSION = 4;
+        private static final int PRIORITY = 100;
+
+        @Override
+        public EmbeddingIndexer createIndexer(final Indexable indexable, final Snapshot snapshot) {
+            if (isIndexable(indexable, snapshot)) {
+                return new AntlrIndexer();
+            } else {
+                return null;
+            }
+        }
+
+        @Override
+        public String getIndexerName() {
+            return NAME;
+        }
+
+        @Override
+        public int getIndexVersion() {
+            return VERSION;
+        }
+
+        private boolean isIndexable(Indexable indexable, Snapshot snapshot) {
+            return Antlr3Language.MIME_TYPE.equals(snapshot.getMimeType())
+                    || Antlr4Language.MIME_TYPE.equals(snapshot.getMimeType());
+        }
+
+        @Override
+        public void filesDeleted(Iterable<? extends Indexable> deleted, Context context) {
+            try {
+                IndexingSupport is = IndexingSupport.getInstance(context);
+                for (Indexable i : deleted) {
+                    is.removeDocuments(i);
+                }
+            } catch (IOException ioe) {
+                LOG.log(Level.WARNING, null, ioe);
+            }
+        }
+
+        @Override
+        public void rootsRemoved(final Iterable<? extends URL> removedRoots) {
+        }
+
+        @Override
+        public void filesDirty(Iterable<? extends Indexable> dirty, Context context) {
+            try {
+                IndexingSupport is = IndexingSupport.getInstance(context);
+                for (Indexable i : dirty) {
+                    is.markDirtyDocuments(i);
+                }
+            } catch (IOException ioe) {
+                LOG.log(Level.WARNING, null, ioe);
+            }
+        }
+
+        @Override
+        public int getPriority() {
+            return PRIORITY;
+        }
+    }
+
+
+    /**
+     * Find all imports of the specified file (including transitive ones).
+     *
+     * @param qs
+     * @param sourceFile
+     * @return
+     */
+    public static List<FileObject> transitiveImports(QuerySupport qs, FileObject sourceFile) {
+        List<FileObject> result = new ArrayList<>();
+        LinkedList<FileObject> toScan = new LinkedList<>();
+        Set<String> seen = new HashSet<>();
+        result.add(sourceFile);
+        toScan.add(sourceFile);
+        seen.add(sourceFile.getName());
+        while(! toScan.isEmpty()) {
+            FileObject target = toScan.poll();
+            try {
+                qs.getQueryFactory()
+                        .file(target)
+                        .execute(AntlrIndexer.FIELD_IMPORT)
+                        .forEach(c -> {
+                            for(String value: c.getValues(AntlrIndexer.FIELD_IMPORT)) {
+                                if(! seen.contains(value)) {
+                                    seen.add(value);
+                                }
+                                FileObject foAntlr;
+                                if(Antlr3Language.MIME_TYPE.equals(sourceFile.getMIMEType())) {
+                                    foAntlr = sourceFile.getParent().getFileObject(value, "g");
+                                } else {
+                                    foAntlr = sourceFile.getParent().getFileObject(value, "g4");
+                                }
+                                if(foAntlr.canRead()) {
+                                    toScan.add(foAntlr);
+                                    result.add(foAntlr);
+                                }
+                            }
+                        });
+            } catch (IOException ex) {
+                Exceptions.printStackTrace(ex);
+            }
+        }
+        return result;
+    }
+
+    private static QuerySupport getQuerySupport(final Collection<FileObject> roots) {
+        try {
+            return QuerySupport.forRoots(
+                    AntlrIndexer.Factory.NAME,
+                    AntlrIndexer.Factory.VERSION,
+                    roots.toArray(new FileObject[0]));
+        } catch (IOException ex) {
+            Exceptions.printStackTrace(ex);
+        }
+        return null;
+    }
+
+    public static QuerySupport getQuerySupport(final FileObject source) {
+        return getQuerySupport(QuerySupport.findRoots(source,
+                null,
+                null,
+                Collections.<String>emptySet()));
+    }
+
+    /**
+     * Create string representation for case insensitive search
+     */
+    private static String ci(String declarationValue) {
+        return declarationValue.toLowerCase(Locale.ENGLISH);
+    }
+}
diff --git a/java/languages.antlr/src/org/netbeans/modules/languages/antlr/AntlrParserResult.java b/java/languages.antlr/src/org/netbeans/modules/languages/antlr/AntlrParserResult.java
index bced56f..e98d7cc 100644
--- a/java/languages.antlr/src/org/netbeans/modules/languages/antlr/AntlrParserResult.java
+++ b/java/languages.antlr/src/org/netbeans/modules/languages/antlr/AntlrParserResult.java
@@ -19,14 +19,10 @@
 package org.netbeans.modules.languages.antlr;
 
 import java.util.ArrayList;
-import java.util.Deque;
-import java.util.HashSet;
-import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import java.util.TreeMap;
-import java.util.concurrent.atomic.AtomicBoolean;
 import org.antlr.v4.runtime.ANTLRErrorListener;
 import org.antlr.v4.runtime.BaseErrorListener;
 import org.antlr.v4.runtime.Parser;
@@ -40,7 +36,6 @@
 import org.netbeans.modules.csl.spi.DefaultError;
 import org.netbeans.modules.csl.spi.ParserResult;
 import org.netbeans.modules.parsing.api.Snapshot;
-import org.netbeans.modules.parsing.api.Source;
 import org.openide.filesystems.FileObject;
 
 /**
@@ -55,54 +50,26 @@
     public final List<OffsetRange> folds = new ArrayList<>();
     public final List<AntlrStructureItem> structure = new ArrayList<>();
 
-    final Set<FileObject> queued = new HashSet<>();
-    final Deque<T> parsingQueue = new LinkedList<>();
-
-    final AtomicBoolean finished = new AtomicBoolean();
+    volatile boolean finished = false;
 
     public AntlrParserResult(Snapshot snapshot) {
         super(snapshot);
-        addParseTask(snapshot, false);
     }
-    
 
     public AntlrParserResult get() {
-        while (!parsingQueue.isEmpty()) {
-            evaluateParser(parsingQueue.removeFirst());
-        }
-        // Second phase scan
-        T secondPhase = createParser(getSnapshot());
-        secondPhase.addParseListener(createOccurancesListener());
-        evaluateParser(secondPhase);
-        finished.set(true);
-        return this;
-    }
-
-    protected final void addParseTask(FileObject fo) {
-        Source src = Source.create(fo);
-        addParseTask(src.createSnapshot(), true);
-    }
-
-
-    protected final void addParseTask(Snapshot snapshot, boolean imported) {
-        // TODO: In a more decent implementation we should not try to parse
-        //       other files. Parser results in other files should be put
-        //       and retrieved by the Indexer infrastructure, which is yet to be
-        //       implemented.
-        FileObject fo = snapshot.getSource().getFileObject();
-        if (queued.add(fo)) {
-            T parser = createParser(snapshot);
-
-
-            if (!imported) {
-                parser.addErrorListener(createErrorListener(fo));
-                parser.addParseListener(createFoldListener());
-            }
+        if (! finished) {
+            FileObject fo = getSnapshot().getSource().getFileObject();
+            T parser = createParser(getSnapshot());
+            parser.addErrorListener(createErrorListener(fo));
+            parser.addParseListener(createFoldListener());
             parser.addParseListener(createReferenceListener(fo));
             parser.addParseListener(createImportListener(fo));
             parser.addParseListener(createStructureListener(fo));
-            parsingQueue.add(parser);
+            parser.addParseListener(createOccurancesListener());
+            evaluateParser(parser);
+            finished = true;
         }
+        return this;
     }
 
     @Override
@@ -117,7 +84,7 @@
 
     @Override
     protected boolean processingFinished() {
-        return finished.get();
+        return finished;
     }
 
     public static class Reference {
diff --git a/java/languages.antlr/src/org/netbeans/modules/languages/antlr/v4/Antlr4ParserResult.java b/java/languages.antlr/src/org/netbeans/modules/languages/antlr/v4/Antlr4ParserResult.java
index c8d927f..9cd8325 100644
--- a/java/languages.antlr/src/org/netbeans/modules/languages/antlr/v4/Antlr4ParserResult.java
+++ b/java/languages.antlr/src/org/netbeans/modules/languages/antlr/v4/Antlr4ParserResult.java
@@ -19,6 +19,7 @@
 package org.netbeans.modules.languages.antlr.v4;
 
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
 import org.antlr.parser.antlr4.ANTLRv4Lexer;
 import org.antlr.parser.antlr4.ANTLRv4Parser;
@@ -40,6 +41,8 @@
  */
 public final class Antlr4ParserResult extends AntlrParserResult<ANTLRv4Parser> {
 
+    private List<String> imports = new ArrayList<>();
+
     public Antlr4ParserResult(Snapshot snapshot) {
         super(snapshot);
     }
@@ -85,10 +88,7 @@
     protected ParseTreeListener createImportListener(FileObject source) {
         return new ANTLRv4ParserBaseListener() {
             private void addImport(String importedGrammar) {
-                FileObject importedFo = source.getParent().getFileObject(importedGrammar + ".g4"); //NOI18N
-                if (importedFo != null) {
-                    addParseTask(importedFo);
-                }
+                imports.add(importedGrammar);
             }
 
             @Override
@@ -235,6 +235,7 @@
         };
     }
 
-
-
+    public List<String> getImports() {
+        return Collections.unmodifiableList(imports);
+    }
 }