You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@netbeans.apache.org by lk...@apache.org on 2022/10/12 18:01:03 UTC
[netbeans] branch master updated: Improve support for ANTLRv4 Grammars (#4773)
This is an automated email from the ASF dual-hosted git repository.
lkishalmi pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/netbeans.git
The following commit(s) were added to refs/heads/master by this push:
new 463fb27299 Improve support for ANTLRv4 Grammars (#4773)
463fb27299 is described below
commit 463fb27299708621ee904e99eb247f33d2aca9a2
Author: Laszlo Kishalmi <la...@gmail.com>
AuthorDate: Wed Oct 12 11:00:57 2022 -0700
Improve support for ANTLRv4 Grammars (#4773)
* Split references and occurrences
* Support mode and channel references
* Better code completion on ANTLRv4 grammars
* Fixed fragment detection and some other stuff
---
.../modules/refactoring/spi/ui/AccessorImpl.java | 4 +-
java/languages.antlr/nbproject/project.xml | 32 +++
.../languages/antlr/AntlrDeclarationFinder.java | 8 +-
...ncesFinder.java => AntlrOccurrencesFinder.java} | 16 +-
.../modules/languages/antlr/AntlrParser.java | 6 +-
.../modules/languages/antlr/AntlrParserResult.java | 33 ++-
.../languages/antlr/AntlrStructureItem.java | 10 +-
.../languages/antlr/AntlrTokenSequence.java | 172 +++++++++++++
.../languages/antlr/refactoring/Refactoring.java | 2 +-
.../Antlr3CompletionProvider.java} | 70 ++----
.../modules/languages/antlr/v3/Antlr3Language.java | 4 +-
.../languages/antlr/v3/Antlr3ParserResult.java | 24 +-
.../antlr/v4/Antlr4CompletionProvider.java | 273 +++++++++++++++++++++
.../modules/languages/antlr/v4/Antlr4Language.java | 2 +-
.../languages/antlr/v4/Antlr4ParserResult.java | 71 ++++--
.../languages/antlr/AntlrTokenSequenceTest.java | 171 +++++++++++++
16 files changed, 788 insertions(+), 110 deletions(-)
diff --git a/ide/refactoring.api/src/org/netbeans/modules/refactoring/spi/ui/AccessorImpl.java b/ide/refactoring.api/src/org/netbeans/modules/refactoring/spi/ui/AccessorImpl.java
index e012b8ef0c..5c4ff90ca2 100644
--- a/ide/refactoring.api/src/org/netbeans/modules/refactoring/spi/ui/AccessorImpl.java
+++ b/ide/refactoring.api/src/org/netbeans/modules/refactoring/spi/ui/AccessorImpl.java
@@ -28,7 +28,9 @@ final class AccessorImpl extends SPIUIAccessor {
@Override
public void reset(FiltersDescription desc) {
- desc.reset();
+ if (desc != null) {
+ desc.reset();
+ }
}
}
diff --git a/java/languages.antlr/nbproject/project.xml b/java/languages.antlr/nbproject/project.xml
index 0088942f64..98efb939e6 100644
--- a/java/languages.antlr/nbproject/project.xml
+++ b/java/languages.antlr/nbproject/project.xml
@@ -25,6 +25,15 @@
<data xmlns="http://www.netbeans.org/ns/nb-module-project/3">
<code-name-base>org.netbeans.modules.languages.antlr</code-name-base>
<module-dependencies>
+ <dependency>
+ <code-name-base>org.netbeans.api.annotations.common</code-name-base>
+ <build-prerequisite/>
+ <compile-dependency/>
+ <run-dependency>
+ <release-version>1</release-version>
+ <specification-version>1.46</specification-version>
+ </run-dependency>
+ </dependency>
<dependency>
<code-name-base>org.netbeans.core.multiview</code-name-base>
<build-prerequisite/>
@@ -86,6 +95,15 @@
<specification-version>1.26</specification-version>
</run-dependency>
</dependency>
+ <dependency>
+ <code-name-base>org.netbeans.modules.editor.indent</code-name-base>
+ <build-prerequisite/>
+ <compile-dependency/>
+ <run-dependency>
+ <release-version>2</release-version>
+ <specification-version>1.61</specification-version>
+ </run-dependency>
+ </dependency>
<dependency>
<code-name-base>org.netbeans.modules.editor.lib2</code-name-base>
<build-prerequisite/>
@@ -212,6 +230,20 @@
</run-dependency>
</dependency>
</module-dependencies>
+ <test-dependencies>
+ <test-type>
+ <name>unit</name>
+ <test-dependency>
+ <code-name-base>org.netbeans.libs.junit4</code-name-base>
+ <compile-dependency/>
+ </test-dependency>
+ <test-dependency>
+ <code-name-base>org.netbeans.modules.nbjunit</code-name-base>
+ <recursive/>
+ <compile-dependency/>
+ </test-dependency>
+ </test-type>
+ </test-dependencies>
<public-packages/>
</data>
</configuration>
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 1fa4169c42..f03c81fb9a 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
@@ -85,12 +85,12 @@ public class AntlrDeclarationFinder implements DeclarationFinder {
}
scannedFiles.add(fo);
- AntlrParserResult result = AntlrParser.getParserResult(fo);
+ AntlrParserResult<?> result = AntlrParser.getParserResult(fo);
- Reference ref = ((Map<String, Reference>) result.references).get(name);
+ Reference ref = result.references.get(name);
- if(ref != null && ref.defOffset != null) {
- AntlrStructureItem asi = new AntlrStructureItem.RuleStructureItem(name, fo, ref.defOffset.getStart(), ref.defOffset.getEnd());
+ if(ref != null && ref.defOffset != OffsetRange.NONE) {
+ AntlrStructureItem asi = new AntlrStructureItem.RuleStructureItem(name, false, fo, ref.defOffset.getStart(), ref.defOffset.getEnd());
DeclarationLocation dln = new DeclarationFinder.DeclarationLocation(fo, ref.defOffset.getStart(), asi);
if (resultDL == DeclarationLocation.NONE) {
resultDL = dln;
diff --git a/java/languages.antlr/src/org/netbeans/modules/languages/antlr/AntlrOccurancesFinder.java b/java/languages.antlr/src/org/netbeans/modules/languages/antlr/AntlrOccurrencesFinder.java
similarity index 81%
rename from java/languages.antlr/src/org/netbeans/modules/languages/antlr/AntlrOccurancesFinder.java
rename to java/languages.antlr/src/org/netbeans/modules/languages/antlr/AntlrOccurrencesFinder.java
index c9ac3e0560..7b4f35b835 100644
--- a/java/languages.antlr/src/org/netbeans/modules/languages/antlr/AntlrOccurancesFinder.java
+++ b/java/languages.antlr/src/org/netbeans/modules/languages/antlr/AntlrOccurrencesFinder.java
@@ -19,6 +19,7 @@
package org.netbeans.modules.languages.antlr;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
import org.netbeans.api.lexer.Token;
import org.netbeans.api.lexer.TokenHierarchy;
@@ -33,7 +34,7 @@ import org.netbeans.modules.parsing.spi.SchedulerEvent;
*
* @author lkishalmi
*/
-public class AntlrOccurancesFinder extends OccurrencesFinder<AntlrParserResult> {
+public class AntlrOccurrencesFinder extends OccurrencesFinder<AntlrParserResult> {
private int caretPosition;
private boolean cancelled;
@@ -82,7 +83,7 @@ public class AntlrOccurancesFinder extends OccurrencesFinder<AntlrParserResult>
return false;
}
- private void computeOccurrences(AntlrParserResult result) {
+ private void computeOccurrences(AntlrParserResult<?> result) {
TokenHierarchy<?> tokenHierarchy = result.getSnapshot().getTokenHierarchy();
TokenSequence<?> ts = tokenHierarchy.tokenSequence();
ts.move(caretPosition);
@@ -91,15 +92,8 @@ public class AntlrOccurancesFinder extends OccurrencesFinder<AntlrParserResult>
Token<?> token = ts.token();
if (token.id() == AntlrTokenId.RULE || token.id() == AntlrTokenId.TOKEN) {
String refName = String.valueOf(token.text());
- Map<String, AntlrParserResult.Reference> refs = result.references;
- AntlrParserResult.Reference ref = refs.get(refName);
- if (ref != null) {
- if(ref.defOffset != null) {
- occurrences.put(ref.defOffset, ColoringAttributes.MARK_OCCURRENCES);
- }
- for (OffsetRange occurance : ref.occurances) {
- occurrences.put(occurance, ColoringAttributes.MARK_OCCURRENCES);
- }
+ for (OffsetRange occurance : result.getOccurrences(refName)) {
+ occurrences.put(occurance, ColoringAttributes.MARK_OCCURRENCES);
}
}
}
diff --git a/java/languages.antlr/src/org/netbeans/modules/languages/antlr/AntlrParser.java b/java/languages.antlr/src/org/netbeans/modules/languages/antlr/AntlrParser.java
index b81ba9e72f..3502a62119 100644
--- a/java/languages.antlr/src/org/netbeans/modules/languages/antlr/AntlrParser.java
+++ b/java/languages.antlr/src/org/netbeans/modules/languages/antlr/AntlrParser.java
@@ -82,8 +82,8 @@ public abstract class AntlrParser extends org.netbeans.modules.parsing.spi.Parse
}
}
- public static AntlrParserResult getParserResult(FileObject fo) {
- AntlrParserResult result = null;
+ public static AntlrParserResult<?> getParserResult(FileObject fo) {
+ AntlrParserResult<?> result = null;
java.lang.ref.Reference<AntlrParserResult> ceReference;
synchronized (CACHE) {
ceReference = CACHE.get(fo);
@@ -94,7 +94,7 @@ public abstract class AntlrParser extends org.netbeans.modules.parsing.spi.Parse
if (result == null) {
try {
- AntlrParserResult[] parserResult = new AntlrParserResult[1];
+ AntlrParserResult<?>[] parserResult = new AntlrParserResult<?>[1];
ParserManager.parse(Collections.singleton(Source.create(fo)), new UserTask() {
@Override
public void run(ResultIterator resultIterator) throws Exception {
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 f903eeb4f9..eaaae99e85 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,6 +19,7 @@
package org.netbeans.modules.languages.antlr;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
@@ -45,14 +46,20 @@ public abstract class AntlrParserResult<T extends Parser> extends ParserResult {
public final List<DefaultError> errors = new ArrayList<>();
public final Map<String, Reference> references = new TreeMap<>();
+ public final Map<String, List<OffsetRange>> occurrences = new HashMap<>();
public final List<OffsetRange> folds = new ArrayList<>();
public final List<AntlrStructureItem> structure = new ArrayList<>();
volatile boolean finished = false;
+ public static final Reference EOF = new Reference("EOF", OffsetRange.NONE); //NOI18N
+
public AntlrParserResult(Snapshot snapshot) {
super(snapshot);
+
+ references.put(EOF.name, EOF);
+
}
public AntlrParserResult get() {
@@ -63,12 +70,12 @@ public abstract class AntlrParserResult<T extends Parser> extends ParserResult {
parser.addParseListener(createFoldListener());
parser.addParseListener(createReferenceListener());
parser.addParseListener(createImportListener());
- parser.addParseListener(createStructureListener());
- parser.addParseListener(createOccurancesListener());
evaluateParser(parser);
// Start a second parsing phase for checking;
parser = createParser(getSnapshot());
+ parser.addParseListener(createStructureListener());
+ parser.addParseListener(createOccurancesListener());
parser.addParseListener(createCheckReferences());
evaluateParser(parser);
finished = true;
@@ -93,13 +100,10 @@ public abstract class AntlrParserResult<T extends Parser> extends ParserResult {
public static class Reference {
public final String name;
- public FileObject source;
- public OffsetRange defOffset;
- public final List<OffsetRange> occurances = new ArrayList<>();
+ public final OffsetRange defOffset;
- public Reference(String name, FileObject source, OffsetRange defOffset) {
+ public Reference(String name, OffsetRange defOffset) {
this.name = name;
- this.source = source;
this.defOffset = defOffset;
}
}
@@ -107,6 +111,21 @@ public abstract class AntlrParserResult<T extends Parser> extends ParserResult {
protected final FileObject getFileObject() {
return getSnapshot().getSource().getFileObject();
}
+
+ public final List<? extends OffsetRange> getOccurrences(String refName) {
+ ArrayList<OffsetRange> ret = new ArrayList<>();
+ if (references.containsKey(refName)) {
+ ret.add(references.get(refName).defOffset);
+ }
+ if (occurrences.containsKey(refName)) {
+ ret.addAll(occurrences.get(refName));
+ }
+ return ret;
+ }
+
+ protected final void markOccurrence(String refName, OffsetRange or) {
+ occurrences.computeIfAbsent(refName, s -> new ArrayList<>()).add(or);
+ }
protected abstract T createParser(Snapshot snapshot);
protected abstract void evaluateParser(T parser);
diff --git a/java/languages.antlr/src/org/netbeans/modules/languages/antlr/AntlrStructureItem.java b/java/languages.antlr/src/org/netbeans/modules/languages/antlr/AntlrStructureItem.java
index e357e2d630..57bf8f4aff 100644
--- a/java/languages.antlr/src/org/netbeans/modules/languages/antlr/AntlrStructureItem.java
+++ b/java/languages.antlr/src/org/netbeans/modules/languages/antlr/AntlrStructureItem.java
@@ -148,8 +148,10 @@ public abstract class AntlrStructureItem implements ElementHandle, StructureItem
public static final class RuleStructureItem extends AntlrStructureItem {
- public RuleStructureItem(String name, FileObject source, int startOffset, int stopOffset) {
+ final boolean fragment;
+ public RuleStructureItem(String name, boolean fragment, FileObject source, int startOffset, int stopOffset) {
super(name, source, startOffset, stopOffset);
+ this.fragment = fragment;
}
@Override
@@ -164,7 +166,11 @@ public abstract class AntlrStructureItem implements ElementHandle, StructureItem
@Override
public ElementKind getKind() {
- return Character.isUpperCase(name.charAt(0)) ? ElementKind.FIELD : ElementKind.RULE;
+ if (fragment) {
+ return ElementKind.CONSTANT;
+ } else {
+ return Character.isUpperCase(name.charAt(0)) ? ElementKind.FIELD : ElementKind.RULE;
+ }
}
}
}
diff --git a/java/languages.antlr/src/org/netbeans/modules/languages/antlr/AntlrTokenSequence.java b/java/languages.antlr/src/org/netbeans/modules/languages/antlr/AntlrTokenSequence.java
new file mode 100644
index 0000000000..785f1ea6ff
--- /dev/null
+++ b/java/languages.antlr/src/org/netbeans/modules/languages/antlr/AntlrTokenSequence.java
@@ -0,0 +1,172 @@
+/*
+ * 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.util.ArrayList;
+import java.util.ListIterator;
+import java.util.Optional;
+import java.util.function.Predicate;
+import org.antlr.v4.runtime.Lexer;
+import static org.antlr.v4.runtime.Recognizer.EOF;
+import org.antlr.v4.runtime.Token;
+import org.antlr.v4.runtime.TokenSource;
+
+/**
+ *
+ * @author lkishalmi
+ */
+public final class AntlrTokenSequence {
+
+ private final TokenSource tokens;
+ private boolean eofRead = false;
+ private int readIndex;
+ private int cursorOffset;
+ private ListIterator<Token> cursor;
+
+ private final ArrayList<Token> tokenList = new ArrayList<>(200);
+
+ public static final Predicate<Token> DEFAULT_CHANNEL = new ChannelFilter(Lexer.DEFAULT_TOKEN_CHANNEL);
+
+ public static final class ChannelFilter implements Predicate<Token> {
+ private final int channel;
+
+ public ChannelFilter(int channel) {
+ this.channel = channel;
+ }
+
+ @Override
+ public boolean test(Token t) {
+ return channel == t.getChannel();
+ }
+ }
+
+ public AntlrTokenSequence(TokenSource tokens) {
+ this.tokens = tokens;
+ this.cursor = tokenList.listIterator();
+ }
+
+ public void seekTo(int offset) {
+ if (offset > readIndex) {
+ if (cursor.hasNext()) {
+ //replace the cursor if it is not at the end of the list.
+ cursor = tokenList.listIterator(tokenList.size());
+ }
+ Token t = read();
+ while ((t != null) && (t.getStopIndex() + 1 < offset)) {
+ t = read();
+ }
+ if (t != null && (offset < t.getStopIndex() + 1)) {
+ cursor.previous();
+ cursorOffset = t.getStartIndex();
+ } else {
+ cursorOffset = readIndex;
+ }
+ } else {
+ if (offset > getOffset()) {
+ next((t) -> t.getStopIndex() > offset);
+ if (cursor.hasPrevious()) {
+ cursor.previous();
+ }
+
+ } else {
+ previous((t) -> t.getStartIndex() > offset);
+ }
+ }
+
+ }
+
+ public boolean isEmpty() {
+ return tokenList.isEmpty() && !hasNext();
+ }
+
+ public boolean hasNext() {
+ if (!eofRead && (cursorOffset == readIndex) && !cursor.hasNext()) {
+ Token t = read();
+ if (t != null) {
+ cursor.previous();
+ }
+ }
+ return !(eofRead && !cursor.hasNext());
+ }
+
+ public boolean hasPrevious() {
+ return cursor.hasPrevious();
+ }
+
+ public int getOffset() {
+ return cursorOffset;
+ }
+
+ public Optional<Token> previous() {
+ Optional<Token> ret = cursor.hasPrevious() ? Optional.of(cursor.previous()) : Optional.empty();
+ cursorOffset = cursor.hasPrevious() ? ret.get().getStartIndex() : 0;
+ return ret;
+
+ }
+
+ public Optional<Token> previous(Predicate<Token> filter) {
+ Optional<Token> ot = previous();
+ while (ot.isPresent() && !filter.test(ot.get())) {
+ ot = previous();
+ }
+ return ot;
+ }
+
+ public Optional<Token> previous(int tokenType){
+ return previous((Token t) -> t.getType() == tokenType);
+ }
+
+ public Optional<Token> next() {
+ if (hasNext()) {
+ Token t = cursor.next();
+ cursorOffset = t.getStopIndex() + 1;
+ return Optional.of(t);
+ } else {
+ return Optional.empty();
+ }
+ }
+
+ public Optional<Token> next(Predicate<Token> filter) {
+ Optional<Token> ot = next();
+ while (ot.isPresent() && !filter.test(ot.get())) {
+ ot = next();
+ }
+ return ot;
+ }
+
+ public Optional<Token> next(int tokenType){
+ return next((Token t) -> t.getType() == tokenType);
+ }
+
+ private Token read() {
+ if (eofRead) {
+ return null;
+ }
+ Token t = tokens.nextToken();
+ if (t.getType() != EOF) {
+ cursor.add(t);
+ readIndex = t.getStopIndex() + 1;
+ return t;
+ } else {
+ eofRead = true;
+ return null;
+ }
+ }
+
+}
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
index 747feaea47..027ec05012 100644
--- 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
@@ -148,8 +148,8 @@ public class Refactoring {
if(ref.defOffset != null) {
ranges.add(ref.defOffset);
}
- ranges.addAll(ref.occurances);
}
+ ranges.addAll(result.getOccurrences(name));
for(OffsetRange or : ranges) {
PositionBounds bounds;
diff --git a/java/languages.antlr/src/org/netbeans/modules/languages/antlr/AntlrCompletionProvider.java b/java/languages.antlr/src/org/netbeans/modules/languages/antlr/v3/Antlr3CompletionProvider.java
similarity index 63%
rename from java/languages.antlr/src/org/netbeans/modules/languages/antlr/AntlrCompletionProvider.java
rename to java/languages.antlr/src/org/netbeans/modules/languages/antlr/v3/Antlr3CompletionProvider.java
index 6f12999ff9..ffa79594b7 100644
--- a/java/languages.antlr/src/org/netbeans/modules/languages/antlr/AntlrCompletionProvider.java
+++ b/java/languages.antlr/src/org/netbeans/modules/languages/antlr/v3/Antlr3CompletionProvider.java
@@ -16,11 +16,9 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.netbeans.modules.languages.antlr;
+package org.netbeans.modules.languages.antlr.v3;
-import java.util.HashSet;
import java.util.Map;
-import java.util.Set;
import java.util.prefs.Preferences;
import javax.swing.text.Document;
import javax.swing.text.JTextComponent;
@@ -28,14 +26,13 @@ import org.netbeans.api.editor.document.EditorDocumentUtils;
import org.netbeans.api.editor.mimelookup.MimeLookup;
import org.netbeans.api.editor.mimelookup.MimePath;
import org.netbeans.api.editor.mimelookup.MimeRegistration;
-import org.netbeans.api.editor.mimelookup.MimeRegistrations;
import org.netbeans.api.editor.settings.SimpleValueNames;
import org.netbeans.api.lexer.Token;
import org.netbeans.api.lexer.TokenHierarchy;
import org.netbeans.api.lexer.TokenSequence;
-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.languages.antlr.AntlrParser;
+import org.netbeans.modules.languages.antlr.AntlrParserResult;
+import org.netbeans.modules.languages.antlr.AntlrTokenId;
import org.netbeans.spi.editor.completion.CompletionItem;
import org.netbeans.spi.editor.completion.CompletionProvider;
import org.netbeans.spi.editor.completion.CompletionResultSet;
@@ -49,11 +46,8 @@ import org.openide.filesystems.FileObject;
*
* @author Laszlo Kishalmi
*/
-@MimeRegistrations({
- @MimeRegistration(mimeType = Antlr3Language.MIME_TYPE, service = CompletionProvider.class),
- @MimeRegistration(mimeType = Antlr4Language.MIME_TYPE, service = CompletionProvider.class),
-})
-public class AntlrCompletionProvider implements CompletionProvider {
+@MimeRegistration(mimeType = Antlr3Language.MIME_TYPE, service = CompletionProvider.class)
+public class Antlr3CompletionProvider implements CompletionProvider {
@Override
public CompletionTask createTask(int queryType, JTextComponent component) {
@@ -106,45 +100,29 @@ public class AntlrCompletionProvider implements CompletionProvider {
return;
}
- Set<FileObject> scannedFiles = new HashSet<>();
- addReferencesForFile(fo, isCaseSensitive, caretOffset - prefix.length(), prefix, resultSet, scannedFiles);
+ String mprefix = isCaseSensitive ? prefix : prefix.toUpperCase();
+
+ AntlrParserResult result = AntlrParser.getParserResult(fo);
+ Map<String, AntlrParserResult.Reference> refs = result.references;
+
+ int startOffset = caretOffset - prefix.length();
+ for (String ref : refs.keySet()) {
+ String mref = isCaseSensitive ? ref : ref.toUpperCase();
+ boolean match = mref.startsWith(mprefix);
+ if (match) {
+ CompletionItem item = CompletionUtilities.newCompletionItemBuilder(ref)
+ .startOffset(startOffset)
+ .leftHtmlText(ref)
+ .sortText(ref)
+ .build();
+ resultSet.addItem(item);
+ }
+ }
} finally {
resultSet.finish();
}
}
- public void addReferencesForFile(FileObject fo, boolean isCaseSensitive, int removeLength, String prefix, CompletionResultSet resultSet, Set<FileObject> scannedFiles) {
- if(scannedFiles.contains(fo)) {
- return;
- }
- scannedFiles.add(fo);
-
- String mprefix = isCaseSensitive ? prefix : prefix.toUpperCase();
-
- AntlrParserResult result = AntlrParser.getParserResult(fo);
- 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)
- .startOffset(removeLength)
- .leftHtmlText(ref)
- .sortText(ref)
- .build();
- resultSet.addItem(item);
- }
- }
-
- if(result instanceof Antlr4ParserResult) {
- for(String s: ((Antlr4ParserResult) result).getImports()) {
- FileObject importedFo = fo.getParent().getFileObject(s, "g4");
- if(importedFo != null) {
- addReferencesForFile(importedFo, isCaseSensitive, removeLength, prefix, resultSet, scannedFiles);
- }
- }
- }
- }
}
}
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 419e91c11c..296990aded 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
@@ -27,7 +27,7 @@ import org.netbeans.modules.csl.api.StructureScanner;
import org.netbeans.modules.csl.spi.DefaultLanguageConfig;
import org.netbeans.modules.csl.spi.LanguageRegistration;
import org.netbeans.modules.languages.antlr.AntlrDeclarationFinder;
-import org.netbeans.modules.languages.antlr.AntlrOccurancesFinder;
+import org.netbeans.modules.languages.antlr.AntlrOccurrencesFinder;
import org.netbeans.modules.languages.antlr.AntlrParser;
import org.netbeans.modules.languages.antlr.AntlrParserResult;
import org.netbeans.modules.languages.antlr.AntlrStructureScanner;
@@ -172,7 +172,7 @@ public final class Antlr3Language extends DefaultLanguageConfig {
@Override
public OccurrencesFinder getOccurrencesFinder() {
- return new AntlrOccurancesFinder();
+ return new AntlrOccurrencesFinder();
}
@Override
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 84c7583fd3..74b325bef8 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
@@ -19,6 +19,7 @@
package org.netbeans.modules.languages.antlr.v3;
import java.util.function.Consumer;
+import java.util.logging.Logger;
import org.antlr.parser.antlr3.ANTLRv3Lexer;
import org.antlr.parser.antlr3.ANTLRv3Parser;
import org.antlr.parser.antlr3.ANTLRv3ParserBaseListener;
@@ -37,6 +38,8 @@ import org.netbeans.modules.parsing.api.Snapshot;
* @author lkishalmi
*/
public final class Antlr3ParserResult extends AntlrParserResult<ANTLRv3Parser> {
+
+ private static final Logger LOG = Logger.getLogger(Antlr3ParserResult.class.getName());
public Antlr3ParserResult(Snapshot snapshot) {
super(snapshot);
@@ -63,12 +66,8 @@ public final class Antlr3ParserResult extends AntlrParserResult<ANTLRv3Parser> {
Token token = ctx.id_().getStart();
OffsetRange range = new OffsetRange(token.getStartIndex(), token.getStopIndex() + 1);
String name = token.getText();
- if (references.containsKey(name)) {
- references.get(name).defOffset = range;
- } else {
- Reference ref = new Reference(name, getFileObject(), range);
- references.put(ref.name, ref);
- }
+ Reference ref = new Reference(name, range);
+ references.put(ref.name, ref);
}
};
}
@@ -77,7 +76,7 @@ public final class Antlr3ParserResult extends AntlrParserResult<ANTLRv3Parser> {
protected ParseTreeListener createCheckReferences() {
return new ANTLRv3OccuranceListener((token) -> {
String name = token.getText();
- if (!"EOF".equals(name) && (!references.containsKey(name) || references.get(name).defOffset == null)) {
+ if (!references.containsKey(name)) {
//TODO: It seems the ANTLRv3 Grammar Occurance finder could be a bit smarter
//Adding the following line could produce false positives.
//errors.add(new DefaultError(null, "Unknown Reference: " + name, null, source, token.getStartIndex(), token.getStopIndex() + 1, Severity.ERROR));
@@ -137,9 +136,10 @@ public final class Antlr3ParserResult extends AntlrParserResult<ANTLRv3Parser> {
@Override
public void exitRule_(ANTLRv3Parser.Rule_Context ctx) {
+ boolean fragment = ctx.FRAGMENT() != null;
if (ctx.id_() != null) {
AntlrStructureItem.RuleStructureItem rule = new AntlrStructureItem.RuleStructureItem(
- ctx.id_().getText(), getFileObject(), ctx.getStart().getStartIndex(), ctx.getStop().getStopIndex() + 1);
+ ctx.id_().getText(), fragment, getFileObject(), ctx.getStart().getStartIndex(), ctx.getStop().getStopIndex() + 1);
structure.add(rule);
}
}
@@ -151,12 +151,8 @@ public final class Antlr3ParserResult extends AntlrParserResult<ANTLRv3Parser> {
protected ParseTreeListener createOccurancesListener() {
return new ANTLRv3OccuranceListener((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);
- }
- ref.occurances.add(new OffsetRange(token.getStartIndex(), token.getStopIndex() + 1));
+ OffsetRange or = new OffsetRange(token.getStartIndex(), token.getStopIndex() + 1);
+ markOccurrence(refName, or);
});
}
diff --git a/java/languages.antlr/src/org/netbeans/modules/languages/antlr/v4/Antlr4CompletionProvider.java b/java/languages.antlr/src/org/netbeans/modules/languages/antlr/v4/Antlr4CompletionProvider.java
new file mode 100644
index 0000000000..b072a74d02
--- /dev/null
+++ b/java/languages.antlr/src/org/netbeans/modules/languages/antlr/v4/Antlr4CompletionProvider.java
@@ -0,0 +1,273 @@
+/*
+ * 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.v4;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import org.netbeans.modules.languages.antlr.*;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.prefs.Preferences;
+import javax.swing.text.AbstractDocument;
+import javax.swing.text.BadLocationException;
+import javax.swing.text.Document;
+import javax.swing.text.JTextComponent;
+import org.antlr.parser.antlr4.ANTLRv4Lexer;
+import org.antlr.v4.runtime.CharStreams;
+import org.antlr.v4.runtime.Token;
+import org.netbeans.api.editor.document.EditorDocumentUtils;
+import org.netbeans.api.editor.document.LineDocument;
+import org.netbeans.api.editor.document.LineDocumentUtils;
+import org.netbeans.api.editor.mimelookup.MimeLookup;
+import org.netbeans.api.editor.mimelookup.MimePath;
+import org.netbeans.api.editor.mimelookup.MimeRegistration;
+import org.netbeans.api.editor.settings.SimpleValueNames;
+import org.netbeans.spi.editor.completion.CompletionItem;
+import org.netbeans.spi.editor.completion.CompletionProvider;
+import org.netbeans.spi.editor.completion.CompletionResultSet;
+import org.netbeans.spi.editor.completion.CompletionTask;
+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.netbeans.api.annotations.common.StaticResource;
+
+import static org.antlr.parser.antlr4.ANTLRv4Lexer.*;
+import static org.netbeans.modules.languages.antlr.AntlrTokenSequence.DEFAULT_CHANNEL;
+
+/**
+ *
+ * @author Laszlo Kishalmi
+ */
+@MimeRegistration(mimeType = Antlr4Language.MIME_TYPE, service = CompletionProvider.class)
+public class Antlr4CompletionProvider implements CompletionProvider {
+
+ @StaticResource
+ private static final String ANTLR_ICON = "org/netbeans/modules/languages/antlr/resources/antlr.png";
+
+ @Override
+ public CompletionTask createTask(int queryType, JTextComponent component) {
+ return new AsyncCompletionTask(new AntlrCompletionQuery(isCaseSensitive()), component);
+ }
+
+ @Override
+ public int getAutoQueryTypes(JTextComponent component, String typedText) {
+ return 0;
+ }
+
+ private static boolean isCaseSensitive() {
+ Preferences prefs = MimeLookup.getLookup(MimePath.EMPTY).lookup(Preferences.class);
+ return prefs.getBoolean(SimpleValueNames.COMPLETION_CASE_SENSITIVE, false);
+ }
+
+ private class AntlrCompletionQuery extends AsyncCompletionQuery {
+
+ final boolean caseSensitive;
+
+ public AntlrCompletionQuery(boolean caseSensitive) {
+ this.caseSensitive = caseSensitive;
+ }
+
+ //TODO: This is a Lexer based pretty dumb implementation. Only offer
+ // prefix if the cursor is at the end of a start of token/lexer rule.
+ // Shall be replaced with a better approach.
+ private String getPrefix(Document doc, int caretOffset, boolean upToOffset) throws BadLocationException {
+ LineDocument lineDoc = LineDocumentUtils.asRequired(doc, LineDocument.class);
+ int start = LineDocumentUtils.getWordStart(lineDoc, caretOffset);
+ int end = LineDocumentUtils.getWordEnd(lineDoc, caretOffset);
+ String prefix = doc.getText(start, (upToOffset ? caretOffset : end) - start);
+
+ return (prefix.length() > 0) && !Character.isWhitespace(prefix.codePointAt(prefix.length() - 1)) ? prefix : "";
+ }
+
+ @Override
+ protected void query(CompletionResultSet resultSet, Document doc, int caretOffset) {
+ AbstractDocument adoc = (AbstractDocument) doc;
+ try {
+ FileObject fo = EditorDocumentUtils.getFileObject(doc);
+ if (fo == null) {
+ return;
+ }
+
+ String prefix = "";
+ adoc.readLock();
+ AntlrTokenSequence tokens;
+ try {
+ String text = doc.getText(0, doc.getLength());
+ tokens = new AntlrTokenSequence(new ANTLRv4Lexer(CharStreams.fromString(text)));
+ } catch (BadLocationException ex) {
+ return;
+ } finally {
+ adoc.readUnlock();
+ }
+
+ if (!tokens.isEmpty()) {
+
+ tokens.seekTo(caretOffset);
+ // Check if we are in a comment (that is filtered out from the token stream)
+
+ int tokenOffset = tokens.getOffset();
+ if (tokens.hasNext()) {
+ Token nt = tokens.next().get();
+ if (caretOffset > tokenOffset) {
+ // Caret is in a token
+ if ((nt.getChannel() == COMMENT) || (nt.getType() == ACTION_CONTENT)) {
+ // We are in comment or action, no code completion
+ return;
+ }
+ if (nt.getChannel() == DEFAULT_TOKEN_CHANNEL) {
+ prefix = nt.getText().substring(0, caretOffset - tokenOffset);
+ }
+ } else if (nt.getType() == ACTION_CONTENT) {
+ //We are in action
+ return;
+ }
+ tokens.previous();
+ lookAround(fo, tokens, caretOffset, prefix, resultSet);
+ } else {
+ //Empty grammar so far offer lexer and grammar
+ addTokens("", caretOffset, resultSet, "lexer", "grammar");
+ }
+ }
+ } catch (Throwable th) {
+ System.out.println(th);
+ } finally {
+ resultSet.finish();
+ }
+ }
+
+ private void lookAround(FileObject fo, AntlrTokenSequence tokens, int caretOffset, String prefix, CompletionResultSet resultSet) {
+ Optional<Token> opt = tokens.previous(DEFAULT_CHANNEL);
+ if (!opt.isPresent()) {
+ //At the start of the file;
+ Optional<Token> t = tokens.next(DEFAULT_CHANNEL);
+ if (t.isPresent() && t.get().getType() != LEXER) {
+ addTokens(prefix, caretOffset, resultSet, "lexer");
+ }
+ if (t.isPresent() && (t.get().getType() != LEXER) && (t.get().getType() != GRAMMAR)) {
+ addTokens(prefix, caretOffset, resultSet, "grammar");
+ }
+ return;
+ } else {
+ Token pt = opt.get();
+ if (((pt.getType() == RULE_REF) || (pt.getType() == TOKEN_REF)) && (caretOffset == pt.getStopIndex() + 1)) {
+ // Could be start of some keywords
+ prefix = pt.getText();
+ opt = tokens.previous(DEFAULT_CHANNEL);
+ }
+ if (!opt.isPresent()) {
+ addTokens(prefix, caretOffset, resultSet, "lexer", "grammar");
+ return;
+ } else {
+ pt = opt.get();
+ switch (pt.getType()) {
+ case LEXER:
+ Optional<Token> t = tokens.next(DEFAULT_CHANNEL);
+ if (!t.isPresent() || t.get().getType() != GRAMMAR) {
+ addTokens(prefix, caretOffset, resultSet, "grammar");
+ }
+ return;
+
+ case SEMI:
+ //Could be the begining of a new rule def.
+ addTokens(prefix, caretOffset, resultSet, "mode", "fragment");
+ return;
+ case RARROW:
+ //Command: offer 'channel', 'skip', etc...
+ addTokens(prefix, caretOffset, resultSet, "skip", "more", "type", "channel", "mode", "pushMode", "popMode");
+ return;
+ default:
+ tokens.seekTo(caretOffset);
+ Optional<Token> semi = tokens.previous(SEMI);
+ tokens.seekTo(caretOffset);
+ Optional<Token> colon = tokens.previous(COLON);
+ if (semi.isPresent() && colon.isPresent()
+ && semi.get().getStartIndex() < colon.get().getStartIndex()) {
+ // we are in lexer/parser ruledef
+
+ Set<FileObject> scanned = new HashSet<>();
+ Map<String,AntlrParserResult.Reference> matchingRefs = new HashMap<>();
+ addReferencesForFile(fo, prefix, matchingRefs, scanned);
+
+ int startOffset = caretOffset - prefix.length();
+ for (AntlrParserResult.Reference ref : matchingRefs.values()) {
+ CompletionItem item = CompletionUtilities.newCompletionItemBuilder(ref.name)
+ .startOffset(startOffset)
+ .leftHtmlText(ref.name)
+ .sortText(ref.name)
+ .build();
+ resultSet.addItem(item);
+
+ }
+ }
+
+ }
+
+ }
+ }
+
+ }
+
+ public void addTokens(String prefix, int caretOffset, CompletionResultSet resultSet, String... tokens) {
+ String uprefix = caseSensitive ? prefix : prefix.toUpperCase();
+ for (String token : tokens) {
+ String utoken = caseSensitive ? token : token.toUpperCase();
+ if (utoken.startsWith(uprefix)) {
+ CompletionItem item = CompletionUtilities.newCompletionItemBuilder(token)
+ .iconResource(ANTLR_ICON)
+ .startOffset(caretOffset - prefix.length())
+ .leftHtmlText(token)
+ .build();
+ resultSet.addItem(item);
+ }
+ }
+ }
+
+ public void addReferencesForFile(FileObject fo, String prefix, Map<String,AntlrParserResult.Reference> matching, Set<FileObject> scannedFiles) {
+ if (scannedFiles.contains(fo)) {
+ return;
+ }
+ scannedFiles.add(fo);
+
+ String mprefix = caseSensitive ? prefix : prefix.toUpperCase();
+
+ AntlrParserResult<?> result = AntlrParser.getParserResult(fo);
+ Map<String, AntlrParserResult.Reference> refs = result.references;
+ for (String ref : refs.keySet()) {
+ String mref = caseSensitive ? ref : ref.toUpperCase();
+ boolean match = mref.startsWith(mprefix);
+ if (match && !matching.containsKey(ref)) {
+ matching.put(ref, refs.get(ref));
+ }
+ }
+
+ if (result instanceof Antlr4ParserResult) {
+ for (String s : ((Antlr4ParserResult) result).getImports()) {
+ FileObject importedFo = fo.getParent().getFileObject(s, "g4");
+ if (importedFo != null) {
+ addReferencesForFile(importedFo, prefix, matching, scannedFiles);
+ }
+ }
+ }
+ }
+
+ }
+}
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 70739c6c18..d411060999 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
@@ -168,7 +168,7 @@ public final class Antlr4Language extends DefaultLanguageConfig {
@Override
public OccurrencesFinder getOccurrencesFinder() {
- return new AntlrOccurancesFinder();
+ return new AntlrOccurrencesFinder();
}
@Override
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 ffffb31cc2..b6823cc56c 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
@@ -24,6 +24,7 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
+import java.util.logging.Logger;
import org.antlr.parser.antlr4.ANTLRv4Lexer;
import org.antlr.parser.antlr4.ANTLRv4Parser;
import org.antlr.parser.antlr4.ANTLRv4ParserBaseListener;
@@ -32,6 +33,7 @@ import org.antlr.v4.runtime.CharStreams;
import org.antlr.v4.runtime.CommonTokenStream;
import org.antlr.v4.runtime.Token;
import org.antlr.v4.runtime.tree.ParseTreeListener;
+import org.antlr.v4.runtime.tree.TerminalNode;
import org.netbeans.modules.csl.api.OffsetRange;
import org.netbeans.modules.csl.api.Severity;
import org.netbeans.modules.csl.spi.DefaultError;
@@ -47,10 +49,14 @@ import org.openide.filesystems.FileObject;
*/
public final class Antlr4ParserResult extends AntlrParserResult<ANTLRv4Parser> {
- private List<String> imports = new ArrayList<>();
+ private final List<String> imports = new ArrayList<>();
+ private static final Logger LOG = Logger.getLogger(Antlr4ParserResult.class.getName());
+ public static final Reference HIDDEN = new Reference("HIDDEN", OffsetRange.NONE);
+
public Antlr4ParserResult(Snapshot snapshot) {
super(snapshot);
+ references.put(HIDDEN.name, HIDDEN);
}
@Override
@@ -66,6 +72,11 @@ public final class Antlr4ParserResult extends AntlrParserResult<ANTLRv4Parser> {
parser.grammarSpec();
}
+ private static Token getIdentifierToken(ANTLRv4Parser.IdentifierContext ctx) {
+ TerminalNode tn = ctx.RULE_REF() != null ? ctx.RULE_REF() : ctx.TOKEN_REF();
+ return tn.getSymbol();
+ }
+
@Override
protected ParseTreeListener createReferenceListener() {
return new ANTLRv4ParserBaseListener() {
@@ -91,15 +102,24 @@ public final class Antlr4ParserResult extends AntlrParserResult<ANTLRv4Parser> {
}
}
+ @Override
+ public void exitChannelsSpec(ANTLRv4Parser.ChannelsSpecContext ctx) {
+ List<ANTLRv4Parser.IdentifierContext> ids = ctx.idList().identifier();
+ for (ANTLRv4Parser.IdentifierContext id : ids) {
+ addReference(getIdentifierToken(id));
+ }
+ }
+
+ @Override
+ public void exitModeSpec(ANTLRv4Parser.ModeSpecContext ctx) {
+ addReference(getIdentifierToken(ctx.identifier()));
+ }
+
public void addReference(Token token) {
OffsetRange range = new OffsetRange(token.getStartIndex(), token.getStopIndex() + 1);
String name = token.getText();
- if(references.containsKey(name)) {
- references.get(name).defOffset = range;
- } else {
- Reference ref = new Reference(name, getFileObject(), range);
- references.put(ref.name, ref);
- }
+ Reference ref = new Reference(name, range);
+ references.put(ref.name, ref);
}
};
@@ -118,7 +138,7 @@ public final class Antlr4ParserResult extends AntlrParserResult<ANTLRv4Parser> {
}
return new ANTLRv4OccuranceListener((token) -> {
String name = token.getText();
- if(!"EOF".equals(name) && (!allRefs.containsKey(name) || (allRefs.get(name).defOffset == null))) {
+ if(!allRefs.containsKey(name)) {
errors.add(new DefaultError(null, "Unknown Reference: " + name, null, getFileObject(), token.getStartIndex(), token.getStopIndex() + 1, Severity.ERROR));
}
});
@@ -211,17 +231,22 @@ public final class Antlr4ParserResult extends AntlrParserResult<ANTLRv4Parser> {
@Override
public void exitLexerRuleSpec(ANTLRv4Parser.LexerRuleSpecContext ctx) {
- if ((ctx.FRAGMENT() == null) && (ctx.TOKEN_REF() != null)) {
+ boolean fragment = ctx.FRAGMENT() != null;
+ if (ctx.TOKEN_REF() != null) {
// Do not represent fragments in the structure
- AntlrStructureItem.RuleStructureItem rule = new AntlrStructureItem.RuleStructureItem(ctx.TOKEN_REF().getText(), getFileObject(), ctx.getStart().getStartIndex(), ctx.getStop().getStopIndex() + 1);
- lexerStructure.add(rule);
+ AntlrStructureItem.RuleStructureItem rule = new AntlrStructureItem.RuleStructureItem(ctx.TOKEN_REF().getText(), fragment, getFileObject(), ctx.getStart().getStartIndex(), ctx.getStop().getStopIndex() + 1);
+ if (fragment) {
+ structure.add(rule);
+ } else {
+ lexerStructure.add(rule);
+ }
}
}
@Override
public void exitParserRuleSpec(ANTLRv4Parser.ParserRuleSpecContext ctx) {
if (ctx.RULE_REF() != null) {
- AntlrStructureItem.RuleStructureItem rule = new AntlrStructureItem.RuleStructureItem(ctx.RULE_REF().getText(), getFileObject(), ctx.getStart().getStartIndex(), ctx.getStop().getStopIndex() + 1);
+ AntlrStructureItem.RuleStructureItem rule = new AntlrStructureItem.RuleStructureItem(ctx.RULE_REF().getText(), false, getFileObject(), ctx.getStart().getStartIndex(), ctx.getStop().getStopIndex() + 1);
structure.add(rule);
}
}
@@ -250,12 +275,8 @@ public final class Antlr4ParserResult extends AntlrParserResult<ANTLRv4Parser> {
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);
- }
- ref.occurances.add(new OffsetRange(token.getStartIndex(), token.getStopIndex() + 1));
+ OffsetRange or = new OffsetRange(token.getStartIndex(), token.getStopIndex() + 1);
+ markOccurrence(refName, or);
}
@Override
@@ -287,5 +308,19 @@ public final class Antlr4ParserResult extends AntlrParserResult<ANTLRv4Parser> {
onOccurance.accept(ctx.RULE_REF().getSymbol());
}
}
+
+ @Override
+ public void exitLexerCommandExpr(ANTLRv4Parser.LexerCommandExprContext ctx) {
+ onOccurance.accept(getIdentifierToken(ctx.identifier()));
+ }
+
+
+ @Override
+ public void exitChannelsSpec(ANTLRv4Parser.ChannelsSpecContext ctx) {
+ List<ANTLRv4Parser.IdentifierContext> ids = ctx.idList().identifier();
+ for (ANTLRv4Parser.IdentifierContext id : ids) {
+ onOccurance.accept(getIdentifierToken(id));
+ }
+ }
}
}
diff --git a/java/languages.antlr/test/unit/src/org/netbeans/modules/languages/antlr/AntlrTokenSequenceTest.java b/java/languages.antlr/test/unit/src/org/netbeans/modules/languages/antlr/AntlrTokenSequenceTest.java
new file mode 100644
index 0000000000..1e3f775a5a
--- /dev/null
+++ b/java/languages.antlr/test/unit/src/org/netbeans/modules/languages/antlr/AntlrTokenSequenceTest.java
@@ -0,0 +1,171 @@
+/*
+ * 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 org.antlr.parser.antlr4.ANTLRv4Lexer;
+import org.antlr.v4.runtime.CharStreams;
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+import static org.netbeans.modules.languages.antlr.AntlrTokenSequence.DEFAULT_CHANNEL;
+/**
+ *
+ * @author lkishalmi
+ */
+public class AntlrTokenSequenceTest {
+
+ public AntlrTokenSequenceTest() {
+ }
+
+ /**
+ * Test of seekTo method, of class AntlrTokenSequence.
+ */
+ @Test
+ public void testSeekTo1() {
+ System.out.println("seekTo");
+ int offset = 0;
+ AntlrTokenSequence instance = sequence("");
+ instance.seekTo(offset);
+ assertTrue(instance.isEmpty());
+ }
+
+ @Test
+ public void testSeekTo2() {
+ System.out.println("seekTo");
+ int offset = 0;
+ AntlrTokenSequence instance = sequence("/**/");
+ instance.seekTo(offset);
+ assertFalse(instance.isEmpty());
+ assertTrue(instance.next().isPresent());
+ }
+
+ @Test
+ public void testSeekTo3() {
+ System.out.println("seekTo");
+ int offset = 4;
+ AntlrTokenSequence instance = sequence("/**/");
+ instance.seekTo(offset);
+ assertFalse(instance.isEmpty());
+ assertFalse(instance.next().isPresent());
+ assertTrue(instance.hasPrevious());
+ }
+
+ @Test
+ public void testSeekTo4() {
+ System.out.println("seekTo");
+ int offset = 5;
+ AntlrTokenSequence instance = sequence("/* */lexer");
+ instance.seekTo(offset);
+ assertFalse(instance.isEmpty());
+ assertTrue(instance.next().isPresent());
+ assertTrue(instance.hasPrevious());
+ }
+
+ @Test
+ public void testSeekTo5() {
+ System.out.println("seekTo");
+ AntlrTokenSequence instance = sequence("/* */lexer");
+ instance.seekTo(10);
+ assertFalse(instance.next().isPresent());
+ instance.seekTo(5);
+ assertFalse(instance.isEmpty());
+ assertTrue(instance.next().isPresent());
+ assertTrue(instance.previous().isPresent());
+ assertFalse(instance.hasPrevious());
+ }
+
+ /**
+ * Test of isEmpty method, of class AntlrTokenSequence.
+ */
+ @Test
+ public void testIsEmpty() {
+ System.out.println("isEmpty");
+ AntlrTokenSequence instance = sequence("");
+ assertTrue(instance.isEmpty());
+ }
+
+ @Test
+ public void testHasNext1() {
+ System.out.println("hasNext");
+ AntlrTokenSequence instance = sequence("lexer");
+ assertTrue(instance.hasNext());
+ }
+
+ @Test
+ public void testHasNext2() {
+ System.out.println("hasNext");
+ AntlrTokenSequence instance = sequence("");
+ assertFalse(instance.hasNext());
+ }
+
+ @Test
+ public void testHasNext3() {
+ System.out.println("hasNext");
+ AntlrTokenSequence instance = sequence("lexer");
+ assertTrue(instance.hasNext());
+ instance.next();
+ assertFalse(instance.hasNext());
+ }
+
+ @Test
+ public void testHasPrevious1() {
+ AntlrTokenSequence instance = sequence("lexer");
+ instance.next();
+ assertTrue(instance.hasPrevious());
+ }
+
+ @Test
+ public void testHasPrevious2() {
+ AntlrTokenSequence instance = sequence("");
+ assertFalse(instance.hasPrevious());
+ }
+
+ @Test
+ public void testGetOffset1() {
+ AntlrTokenSequence instance = sequence("/* */lexer");
+ instance.seekTo(7);
+ assertTrue(instance.hasNext());
+ assertEquals(5, instance.getOffset());
+ instance.previous();
+ assertEquals(0, instance.getOffset());
+ }
+
+ @Test
+ public void testNextPredicate1() {
+ AntlrTokenSequence instance = sequence("/* */lexer grammar");
+ assertTrue(instance.hasNext());
+ instance.next(DEFAULT_CHANNEL).ifPresent((t) -> assertEquals("lexer", t.getText()));
+ instance.next(DEFAULT_CHANNEL).ifPresent((t) -> assertEquals("grammar", t.getText()));
+ assertFalse(instance.hasNext());
+ }
+
+ @Test
+ public void testPreviousPredicate1() {
+ AntlrTokenSequence instance = sequence("/* */lexer grammar");
+ instance.seekTo(18);
+ assertFalse(instance.hasNext());
+ instance.previous(DEFAULT_CHANNEL).ifPresent((t) -> assertEquals("grammar", t.getText()));
+ instance.previous(DEFAULT_CHANNEL).ifPresent((t) -> assertEquals("lexer", t.getText()));
+ assertTrue(instance.hasPrevious());
+ }
+
+ private AntlrTokenSequence sequence(String s) {
+ return new AntlrTokenSequence(new ANTLRv4Lexer(CharStreams.fromString(s)));
+ }
+}
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@netbeans.apache.org
For additional commands, e-mail: commits-help@netbeans.apache.org
For further information about the NetBeans mailing lists, visit:
https://cwiki.apache.org/confluence/display/NETBEANS/Mailing+lists