Implement "Find Usages"
diff --git a/java/languages.antlr/nbproject/project.xml b/java/languages.antlr/nbproject/project.xml
index 7de043e..0088942 100644
--- a/java/languages.antlr/nbproject/project.xml
+++ b/java/languages.antlr/nbproject/project.xml
@@ -61,6 +61,15 @@
                     </run-dependency>
                 </dependency>
                 <dependency>
+                    <code-name-base>org.netbeans.modules.editor</code-name-base>
+                    <build-prerequisite/>
+                    <compile-dependency/>
+                    <run-dependency>
+                        <release-version>3</release-version>
+                        <specification-version>1.105</specification-version>
+                    </run-dependency>
+                </dependency>
+                <dependency>
                     <code-name-base>org.netbeans.modules.editor.completion</code-name-base>
                     <build-prerequisite/>
                     <compile-dependency/>
@@ -123,6 +132,14 @@
                     </run-dependency>
                 </dependency>
                 <dependency>
+                    <code-name-base>org.netbeans.modules.refactoring.api</code-name-base>
+                    <build-prerequisite/>
+                    <compile-dependency/>
+                    <run-dependency>
+                        <specification-version>1.65</specification-version>
+                    </run-dependency>
+                </dependency>
+                <dependency>
                     <code-name-base>org.openide.awt</code-name-base>
                     <build-prerequisite/>
                     <compile-dependency/>
@@ -155,6 +172,14 @@
                     </run-dependency>
                 </dependency>
                 <dependency>
+                    <code-name-base>org.openide.text</code-name-base>
+                    <build-prerequisite/>
+                    <compile-dependency/>
+                    <run-dependency>
+                        <specification-version>6.86</specification-version>
+                    </run-dependency>
+                </dependency>
+                <dependency>
                     <code-name-base>org.openide.util</code-name-base>
                     <build-prerequisite/>
                     <compile-dependency/>
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 793dc94..45e5028 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
@@ -87,10 +87,9 @@
 
         AntlrParserResult result = AntlrLocalIndex.getParserResult(fo);
 
-        Map<String, Reference> refs = result.references;
+        Reference ref = ((Map<String, Reference>) result.references).get(name);
 
-        if(refs.containsKey(name)) {
-            Reference ref = refs.get(name);
+        if(ref != null && ref.defOffset != null) {
             AntlrStructureItem asi = new AntlrStructureItem.RuleStructureItem(name, fo, ref.defOffset.getStart(), ref.defOffset.getEnd());
             DeclarationLocation dln = new DeclarationFinder.DeclarationLocation(fo, ref.defOffset.getStart(), asi);
             if (resultDL == DeclarationLocation.NONE) {
diff --git a/java/languages.antlr/src/org/netbeans/modules/languages/antlr/AntlrOccurancesFinder.java b/java/languages.antlr/src/org/netbeans/modules/languages/antlr/AntlrOccurancesFinder.java
index ec2e24b..c9ac3e0 100644
--- a/java/languages.antlr/src/org/netbeans/modules/languages/antlr/AntlrOccurancesFinder.java
+++ b/java/languages.antlr/src/org/netbeans/modules/languages/antlr/AntlrOccurancesFinder.java
@@ -94,7 +94,9 @@
                 Map<String, AntlrParserResult.Reference> refs = result.references;
                 AntlrParserResult.Reference ref = refs.get(refName);
                 if (ref != null) {
-                    occurrences.put(ref.defOffset, ColoringAttributes.MARK_OCCURRENCES);
+                    if(ref.defOffset != null) {
+                        occurrences.put(ref.defOffset, ColoringAttributes.MARK_OCCURRENCES);
+                    }
                     for (OffsetRange occurance : ref.occurances) {
                         occurrences.put(occurance, ColoringAttributes.MARK_OCCURRENCES);
                     }
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 e98d7cc..8afc040 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
@@ -89,8 +89,8 @@
 
     public static class Reference {
         public final String name;
-        public final FileObject source;
-        public final OffsetRange defOffset;
+        public FileObject source;
+        public OffsetRange defOffset;
         public final List<OffsetRange> occurances = new ArrayList<>();
 
         public Reference(String name, FileObject source, OffsetRange defOffset) {
diff --git a/java/languages.antlr/src/org/netbeans/modules/languages/antlr/refactoring/Refactoring.java b/java/languages.antlr/src/org/netbeans/modules/languages/antlr/refactoring/Refactoring.java
new file mode 100644
index 0000000..11210cd
--- /dev/null
+++ b/java/languages.antlr/src/org/netbeans/modules/languages/antlr/refactoring/Refactoring.java
@@ -0,0 +1,261 @@
+/*
+ * 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.refactoring;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.TreeSet;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.atomic.AtomicBoolean;
+import javax.swing.text.BadLocationException;
+import javax.swing.text.Position;
+import javax.swing.text.StyledDocument;
+import org.netbeans.api.editor.document.LineDocument;
+import org.netbeans.api.editor.document.LineDocumentUtils;
+import org.netbeans.modules.csl.api.OffsetRange;
+import org.netbeans.modules.languages.antlr.AntlrLocalIndex;
+import org.netbeans.modules.languages.antlr.AntlrParserResult;
+import org.netbeans.modules.languages.antlr.AntlrParserResult.Reference;
+import org.netbeans.modules.languages.antlr.v4.Antlr4Language;
+import org.netbeans.modules.languages.antlr.v4.Antlr4ParserResult;
+import org.netbeans.modules.refactoring.api.AbstractRefactoring;
+import org.netbeans.modules.refactoring.api.Problem;
+import org.netbeans.modules.refactoring.api.WhereUsedQuery;
+import org.netbeans.modules.refactoring.spi.RefactoringElementsBag;
+import org.netbeans.modules.refactoring.spi.RefactoringPlugin;
+import org.netbeans.modules.refactoring.spi.RefactoringPluginFactory;
+import org.netbeans.modules.refactoring.spi.SimpleRefactoringElementImplementation;
+import org.openide.cookies.EditorCookie;
+import org.openide.filesystems.FileObject;
+import org.openide.text.CloneableEditorSupport;
+import org.openide.text.PositionBounds;
+import org.openide.util.Exceptions;
+import org.openide.util.Lookup;
+import org.openide.util.NbBundle;
+import org.openide.util.lookup.ServiceProvider;
+
+/**
+ *
+     * @author lahvac
+ */
+@NbBundle.Messages("TXT_Canceled=Canceled")
+public class Refactoring {
+
+    private static final class WhereUsedRefactoringPlugin implements RefactoringPlugin {
+
+        private final WhereUsedQuery query;
+        private final SymbolInformation symbolInformation;
+        private final AtomicBoolean cancel = new AtomicBoolean();
+
+        public WhereUsedRefactoringPlugin(WhereUsedQuery query, SymbolInformation symbolInformation) {
+            this.query = query;
+            this.symbolInformation = symbolInformation;
+        }
+
+        @Override
+        public Problem preCheck() {
+            return null;
+        }
+
+        @Override
+        public Problem checkParameters() {
+            return null;
+        }
+
+        @Override
+        public Problem fastCheckParameters() {
+            return null;
+        }
+
+        @Override
+        public void cancelRequest() {
+            cancel.set(true);
+        }
+
+        @Override
+        public Problem prepare(RefactoringElementsBag refactoringElements) {
+            try {
+                String name = this.symbolInformation.getName();
+                FileObject sourceFO = this.symbolInformation.getSourceFile();
+                Map<FileObject,Set<FileObject>> imports = new HashMap<>();
+
+                if (Antlr4Language.MIME_TYPE.equals(sourceFO.getMIMEType())) {
+                    FileObject parent = sourceFO.getParent();
+                    for(FileObject cf: parent.getChildren()) {
+                        if (cancel.get()) {
+                            throw new CancellationException();
+                        }
+
+                        Antlr4ParserResult result = (Antlr4ParserResult) AntlrLocalIndex.getParserResult(cf);
+
+                        result.getImports().forEach(s -> {
+                            FileObject referencedFO = parent.getFileObject(s, "g4");
+                            if(referencedFO != null) {
+                                imports.computeIfAbsent(cf, cd2 -> new HashSet<>())
+                                        .add(referencedFO);
+                            }
+                        });
+                    }
+                }
+
+                List<FileObject> toScan = new ArrayList<>();
+                Set<FileObject> scannedFileObjects = new HashSet<>();
+
+                toScan.add(sourceFO);
+
+                while(! toScan.isEmpty()) {
+                    if (cancel.get()) {
+                        throw new CancellationException();
+                    }
+
+                    FileObject fo = toScan.remove(0);
+                    if(scannedFileObjects.contains(fo)) {
+                        continue;
+                    }
+                    scannedFileObjects.add(fo);
+
+                    AntlrParserResult<?> result = (AntlrParserResult) AntlrLocalIndex.getParserResult(fo);
+
+                    Reference ref = result.references.get(name);
+
+                    TreeSet<OffsetRange> ranges = new TreeSet<>();
+
+                    if(ref != null) {
+                        if(ref.defOffset != null) {
+                            ranges.add(ref.defOffset);
+                        }
+                        ranges.addAll(ref.occurances);
+                    }
+
+                    for(OffsetRange or : ranges) {
+                        PositionBounds bounds;
+                        try {
+                            CloneableEditorSupport es = fo.getLookup().lookup(CloneableEditorSupport.class);
+                            EditorCookie ec = fo.getLookup().lookup(EditorCookie.class);
+                            StyledDocument doc = ec.openDocument();
+                            LineDocument ldoc = (LineDocument) doc;
+
+                            int rowStart = LineDocumentUtils.getLineStart(ldoc, or.getStart());
+                            int rowEnd = LineDocumentUtils.getLineEnd(ldoc, or.getEnd());
+
+                            bounds = new PositionBounds(
+                                    es.createPositionRef(or.getStart(), Position.Bias.Forward),
+                                    es.createPositionRef(or.getEnd(), Position.Bias.Forward)
+                            );
+
+                            String lineText = doc.getText(rowStart, rowEnd - rowStart);
+                            String annotatedLine =
+                                    lineText.substring(0, or.getStart() - rowStart)
+                                    + "<strong>" 
+                                    + lineText.substring(or.getStart() - rowStart, or.getEnd() - rowStart)
+                                    + "</strong>"
+                                    + lineText.substring(or.getEnd() - rowStart);
+                            refactoringElements.add(query, new AntlrRefactoringElementImpl(annotatedLine, fo, bounds));
+                        } catch (BadLocationException | IOException ex) {
+                            Exceptions.printStackTrace(ex);
+                            bounds = null;
+                        }
+                    }
+
+                    toScan.addAll(imports.getOrDefault(fo, Collections.emptySet()));
+
+                    for(Entry<FileObject,Set<FileObject>> e: imports.entrySet()) {
+                        if(e.getValue().contains(fo)) {
+                            toScan.add(e.getKey());
+                        }
+                    }
+                }
+
+                return null;
+            } catch (CancellationException ex) {
+                return new Problem(false, Bundle.TXT_Canceled());
+            }
+        }
+
+    }
+
+    @ServiceProvider(service=RefactoringPluginFactory.class)
+    public static class FactoryImpl implements RefactoringPluginFactory {
+
+        @Override
+        public RefactoringPlugin createInstance(AbstractRefactoring refactoring) {
+            if (refactoring instanceof WhereUsedQuery) {
+                WhereUsedQuery q = (WhereUsedQuery) refactoring;
+                SymbolInformation symbolInformation = q.getRefactoringSource().lookup(SymbolInformation.class);
+                if (symbolInformation != null) {
+                    return new WhereUsedRefactoringPlugin(q, symbolInformation);
+                }
+            }
+            return null;
+        }
+
+    }
+
+    public static class AntlrRefactoringElementImpl extends SimpleRefactoringElementImplementation {
+
+        private final String annotatedLine;
+        private final FileObject file;
+        private final PositionBounds bounds;
+
+        public AntlrRefactoringElementImpl(String annotatedLine, FileObject file, PositionBounds bounds) {
+            this.annotatedLine = annotatedLine;
+            this.file = file;
+            this.bounds = bounds;
+        }
+
+        @Override
+        public String getText() {
+            return "Element usage";
+        }
+
+        @Override
+        public String getDisplayText() {
+            return annotatedLine;
+        }
+
+        @Override
+        public void performChange() {
+            // Currently the AntlrRefactoringElementImpl is only used for the
+            // WhereUsedRefactoring, which is not doing changes
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public Lookup getLookup() {
+            return Lookup.EMPTY;
+        }
+
+        @Override
+        public FileObject getParentFile() {
+            return file;
+        }
+
+        @Override
+        public PositionBounds getPosition() {
+            return bounds;
+        }
+    }
+}
diff --git a/java/languages.antlr/src/org/netbeans/modules/languages/antlr/refactoring/RefactoringActionsProvider.java b/java/languages.antlr/src/org/netbeans/modules/languages/antlr/refactoring/RefactoringActionsProvider.java
new file mode 100644
index 0000000..52da319
--- /dev/null
+++ b/java/languages.antlr/src/org/netbeans/modules/languages/antlr/refactoring/RefactoringActionsProvider.java
@@ -0,0 +1,113 @@
+/*
+ * 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.refactoring;
+
+import javax.swing.Action;
+import javax.swing.JEditorPane;
+import javax.swing.SwingUtilities;
+import javax.swing.text.AbstractDocument;
+import javax.swing.text.Document;
+import org.netbeans.api.lexer.TokenHierarchy;
+import org.netbeans.api.lexer.TokenSequence;
+import org.netbeans.modules.editor.NbEditorUtilities;
+import org.netbeans.modules.languages.antlr.v3.Antlr3Language;
+import org.netbeans.modules.languages.antlr.v4.Antlr4Language;
+import org.netbeans.modules.refactoring.spi.ui.ActionsImplementationProvider;
+import org.netbeans.modules.refactoring.spi.ui.UI;
+import org.openide.cookies.*;
+import org.openide.filesystems.FileObject;
+import org.openide.text.CloneableEditorSupport;
+import org.openide.text.NbDocument;
+import org.openide.util.Lookup;
+import org.openide.util.NbBundle;
+import org.openide.util.NbBundle.Messages;
+import org.openide.windows.TopComponent;
+
+
+@NbBundle.Messages({"WARN_CannotPerformHere=Cannot perform rename here."})
+@org.openide.util.lookup.ServiceProvider(service=org.netbeans.modules.refactoring.spi.ui.ActionsImplementationProvider.class, position=150)
+public class RefactoringActionsProvider extends ActionsImplementationProvider{
+
+    @Override
+    @Messages("NM_Unknown=Unknown")
+    public void doFindUsages(Lookup lookup) {
+        Runnable start = () -> {
+            EditorCookie ec = lookup.lookup(EditorCookie.class);
+
+            if (isFromEditor(ec)) {
+                JEditorPane c = ec.getOpenedPanes()[0];
+                Document doc = c.getDocument();
+                AbstractDocument abstractDoc = (doc instanceof AbstractDocument) ? ((AbstractDocument) doc) : null;
+                FileObject file = NbEditorUtilities.getFileObject(doc);
+                int caretPos = c.getCaretPosition();
+
+                String name = Bundle.NM_Unknown();
+
+                if (abstractDoc != null) {
+                    abstractDoc.readLock();
+                }
+                try {
+                    TokenSequence<?> ts = TokenHierarchy.get(doc).tokenSequence();
+                    if (ts != null) {
+                        ts.move(caretPos);
+                        if (ts.moveNext()) {
+                            name = ts.token().text().toString();
+                        }
+                    }
+                } finally {
+                    if (abstractDoc != null) {
+                        abstractDoc.readUnlock();
+                    }
+                }
+
+                SymbolInformation si = new SymbolInformation(file, name);
+                UI.openRefactoringUI(new WhereUsedRefactoringUIImpl(si),
+                                     TopComponent.getRegistry().getActivated());
+            }
+        };
+        SwingUtilities.invokeLater(start);
+    }
+
+    static String getActionName(Action action) {
+        String arg = (String) action.getValue(Action.NAME);
+        arg = arg.replace("&", ""); // NOI18N
+        return arg.replace("...", ""); // NOI18N
+    }
+
+    @Override
+    public boolean canFindUsages(Lookup lookup) {
+        EditorCookie ec = lookup.lookup(EditorCookie.class);
+        Document doc = ec.getDocument();
+        FileObject file = NbEditorUtilities.getFileObject(doc);
+        return Antlr3Language.MIME_TYPE.equals(file.getMIMEType())
+                || Antlr4Language.MIME_TYPE.equals(file.getMIMEType());
+    }
+
+    public static boolean isFromEditor(EditorCookie ec) {
+        if (ec != null && NbDocument.findRecentEditorPane(ec) != null) {
+            TopComponent activetc = TopComponent.getRegistry().getActivated();
+            if (activetc instanceof CloneableEditorSupport.Pane) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+}
diff --git a/java/languages.antlr/src/org/netbeans/modules/languages/antlr/refactoring/SymbolInformation.java b/java/languages.antlr/src/org/netbeans/modules/languages/antlr/refactoring/SymbolInformation.java
new file mode 100644
index 0000000..ef39741
--- /dev/null
+++ b/java/languages.antlr/src/org/netbeans/modules/languages/antlr/refactoring/SymbolInformation.java
@@ -0,0 +1,67 @@
+/*
+ * 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.refactoring;
+
+import java.util.Objects;
+import org.openide.filesystems.FileObject;
+
+public class SymbolInformation {
+    private final FileObject sourceFile;
+    private final String name;
+
+    public SymbolInformation(FileObject sourceFile, String name) {
+        this.sourceFile = sourceFile;
+        this.name = name;
+    }
+
+    public FileObject getSourceFile() {
+        return sourceFile;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    @Override
+    public int hashCode() {
+        int hash = 7;
+        hash = 53 * hash + Objects.hashCode(this.sourceFile);
+        hash = 53 * hash + Objects.hashCode(this.name);
+        return hash;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+        final SymbolInformation other = (SymbolInformation) obj;
+        if (!Objects.equals(this.name, other.name)) {
+            return false;
+        }
+        return Objects.equals(this.sourceFile, other.sourceFile);
+    }
+
+}
diff --git a/java/languages.antlr/src/org/netbeans/modules/languages/antlr/refactoring/WhereUsedRefactoringUIImpl.java b/java/languages.antlr/src/org/netbeans/modules/languages/antlr/refactoring/WhereUsedRefactoringUIImpl.java
new file mode 100644
index 0000000..d219c15
--- /dev/null
+++ b/java/languages.antlr/src/org/netbeans/modules/languages/antlr/refactoring/WhereUsedRefactoringUIImpl.java
@@ -0,0 +1,88 @@
+/*
+ * 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.refactoring;
+
+import javax.swing.event.ChangeListener;
+import org.netbeans.modules.refactoring.api.AbstractRefactoring;
+import org.netbeans.modules.refactoring.api.Problem;
+import org.netbeans.modules.refactoring.api.WhereUsedQuery;
+import org.netbeans.modules.refactoring.spi.ui.CustomRefactoringPanel;
+import org.netbeans.modules.refactoring.spi.ui.RefactoringUI;
+import org.openide.util.HelpCtx;
+import org.openide.util.NbBundle.Messages;
+import org.openide.util.lookup.Lookups;
+
+
+public class WhereUsedRefactoringUIImpl implements RefactoringUI {
+    private final SymbolInformation symbolInformation;
+
+    public WhereUsedRefactoringUIImpl(SymbolInformation symbolInformation) {
+        this.symbolInformation = symbolInformation;
+    }
+
+    @Override
+    public String getName() {
+        return symbolInformation.getName();
+    }
+
+    @Override
+    @Messages({
+        "# {0} - identifier",
+        "DESC_Usages=Usages of {0}"
+    })
+    public String getDescription() {
+        return Bundle.DESC_Usages(symbolInformation.getName());
+    }
+
+    @Override
+    public boolean isQuery() {
+        return true;
+    }
+
+    @Override
+    public CustomRefactoringPanel getPanel(ChangeListener parent) {
+        return null;
+    }
+
+    @Override
+    public Problem setParameters() {
+        return null;
+    }
+
+    @Override
+    public Problem checkParameters() {
+        return null;
+    }
+
+    @Override
+    public boolean hasParameters() {
+        return false;
+    }
+
+    @Override
+    public AbstractRefactoring getRefactoring() {
+        return new WhereUsedQuery(Lookups.fixed(symbolInformation));
+    }
+
+    @Override
+    public HelpCtx getHelpCtx() {
+        return HelpCtx.DEFAULT_HELP;
+    }
+
+}
diff --git a/java/languages.antlr/src/org/netbeans/modules/languages/antlr/v3/Antlr3Language.java b/java/languages.antlr/src/org/netbeans/modules/languages/antlr/v3/Antlr3Language.java
index d5d7f1a..419e91c 100644
--- a/java/languages.antlr/src/org/netbeans/modules/languages/antlr/v3/Antlr3Language.java
+++ b/java/languages.antlr/src/org/netbeans/modules/languages/antlr/v3/Antlr3Language.java
@@ -112,7 +112,12 @@
             path = "Loaders/text/x-antlr3/Actions",
             id = @ActionID(category = "System", id = "org.openide.actions.PropertiesAction"),
             position = 1500
-    )
+    ),
+    @ActionReference(
+            path = "Editors/text/x-antlr3/Popup",
+            id = @ActionID(category = "Refactoring", id = "org.netbeans.modules.refactoring.api.ui.WhereUsedAction"),
+            position = 1600
+    ),
 })
 
 @LanguageRegistration(mimeType = Antlr3Language.MIME_TYPE, useMultiview = true)
diff --git a/java/languages.antlr/src/org/netbeans/modules/languages/antlr/v3/Antlr3ParserResult.java b/java/languages.antlr/src/org/netbeans/modules/languages/antlr/v3/Antlr3ParserResult.java
index 138f37d..a773a30 100644
--- a/java/languages.antlr/src/org/netbeans/modules/languages/antlr/v3/Antlr3ParserResult.java
+++ b/java/languages.antlr/src/org/netbeans/modules/languages/antlr/v3/Antlr3ParserResult.java
@@ -63,8 +63,13 @@
             public void exitRule_(ANTLRv3Parser.Rule_Context ctx) {
                 Token token = ctx.id_().getStart();
                 OffsetRange range = new OffsetRange(token.getStartIndex(), token.getStopIndex() + 1);
-                Reference ref = new Reference(token.getText(), source, range);
-                references.put(ref.name, ref);
+                String name = token.getText();
+                if (references.containsKey(name)) {
+                    references.get(name).defOffset = range;
+                } else {
+                    Reference ref = new Reference(name, source, range);
+                    references.put(ref.name, ref);
+                }
             }
 
         };
@@ -139,6 +144,10 @@
             private void addOccurance(Token token) {
                 String refName = token.getText();
                 Reference ref = references.get(refName);
+                if (ref == null) {
+                    ref = new Reference(refName, getSnapshot().getSource().getFileObject(), null);
+                    references.put(ref.name, ref);
+                }
                 if (ref != null) {
                     ref.occurances.add(new OffsetRange(token.getStartIndex(), token.getStopIndex() + 1));
                 }
diff --git a/java/languages.antlr/src/org/netbeans/modules/languages/antlr/v4/Antlr4Language.java b/java/languages.antlr/src/org/netbeans/modules/languages/antlr/v4/Antlr4Language.java
index f31e2b3..70739c6 100644
--- a/java/languages.antlr/src/org/netbeans/modules/languages/antlr/v4/Antlr4Language.java
+++ b/java/languages.antlr/src/org/netbeans/modules/languages/antlr/v4/Antlr4Language.java
@@ -108,7 +108,12 @@
             path = "Loaders/text/x-antlr4/Actions",
             id = @ActionID(category = "System", id = "org.openide.actions.PropertiesAction"),
             position = 1500
-    )
+    ),
+    @ActionReference(
+            path = "Editors/text/x-antlr4/Popup",
+            id = @ActionID(category = "Refactoring", id = "org.netbeans.modules.refactoring.api.ui.WhereUsedAction"),
+            position = 1600
+    ),
 })
 
 @LanguageRegistration(mimeType = Antlr4Language.MIME_TYPE, useMultiview = true)
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 9cd8325..ebcf7aa 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
@@ -66,17 +66,24 @@
             @Override
             public void exitParserRuleSpec(ANTLRv4Parser.ParserRuleSpecContext ctx) {
                 Token token = ctx.RULE_REF().getSymbol();
-                OffsetRange range = new OffsetRange(token.getStartIndex(), token.getStopIndex() + 1);
-                Reference ref = new Reference(token.getText(), source, range);
-                references.put(ref.name, ref);
+                addReference(token);
             }
 
             @Override
             public void exitLexerRuleSpec(ANTLRv4Parser.LexerRuleSpecContext ctx) {
                 Token token = ctx.TOKEN_REF().getSymbol();
+                addReference(token);
+            }
+
+            public void addReference(Token token) {
                 OffsetRange range = new OffsetRange(token.getStartIndex(), token.getStopIndex() + 1);
-                Reference ref = new Reference(token.getText(), source, range);
-                references.put(ref.name, ref);
+                String name = token.getText();
+                if(references.containsKey(name)) {
+                    references.get(name).defOffset = range;
+                } else {
+                    Reference ref = new Reference(name, source, range);
+                    references.put(ref.name, ref);
+                }
             }
 
         };
@@ -213,6 +220,10 @@
             private void addOccurance(Token token) {
                 String refName = token.getText();
                 Reference ref = references.get(refName);
+                if (ref == null) {
+                    ref = new Reference(refName, getSnapshot().getSource().getFileObject(), null);
+                    references.put(ref.name, ref);
+                }
                 if (ref != null) {
                     ref.occurances.add(new OffsetRange(token.getStartIndex(), token.getStopIndex() + 1));
                 }