Skip to content

Commit abb1a97

Browse files
authored
ANTLRv4 indent and code snippet support. (#4800)
* Add indenting support for ANTLRv4 grammars * Removed console error listeners, fixed a couple of potential NPE-s * Added code snippets for ANTLR v4 Grammars * ANTLR indent set to 4 be default
1 parent ceac8ff commit abb1a97

File tree

10 files changed

+314
-28
lines changed

10 files changed

+314
-28
lines changed

java/languages.antlr/src/org/netbeans/modules/languages/antlr/AntlrParserResult.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,6 @@ public AntlrParserResult(Snapshot snapshot) {
6464

6565
public AntlrParserResult get() {
6666
if (! finished) {
67-
FileObject fo = getSnapshot().getSource().getFileObject();
6867
T parser = createParser(getSnapshot());
6968
parser.addErrorListener(createErrorListener());
7069
parser.addParseListener(createFoldListener());

java/languages.antlr/src/org/netbeans/modules/languages/antlr/layer.xml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,11 @@
4141
</folder>
4242
</folder>
4343
</folder>
44+
<folder name="Preferences">
45+
<folder name="Defaults">
46+
<file name="org-netbeans-modules-languages-antlr-v3-preferences.xml" url="v3/preferences.xml"/>
47+
</folder>
48+
</folder>
4449
</folder>
4550
<folder name="x-antlr4">
4651
<attr name="displayName" bundlevalue="org.netbeans.modules.languages.antlr.Bundle#Editors/text/x-antlr4"/>
@@ -60,6 +65,14 @@
6065
</folder>
6166
</folder>
6267
</folder>
68+
<folder name="CodeTemplates">
69+
<file name="org-netbeans-modules-languages-antlr-v4-snippets.xml" url="v4/snippets.xml"/>
70+
</folder>
71+
<folder name="Preferences">
72+
<folder name="Defaults">
73+
<file name="org-netbeans-modules-languages-antlr-v4-preferences.xml" url="v4/preferences.xml"/>
74+
</folder>
75+
</folder>
6376
</folder>
6477
</folder>
6578
</folder>

java/languages.antlr/src/org/netbeans/modules/languages/antlr/v3/Antlr3ParserResult.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import org.antlr.v4.runtime.CharStream;
2727
import org.antlr.v4.runtime.CharStreams;
2828
import org.antlr.v4.runtime.CommonTokenStream;
29+
import org.antlr.v4.runtime.ConsoleErrorListener;
2930
import org.antlr.v4.runtime.Token;
3031
import org.antlr.v4.runtime.tree.ParseTreeListener;
3132
import org.netbeans.modules.csl.api.OffsetRange;
@@ -50,7 +51,9 @@ protected ANTLRv3Parser createParser(Snapshot snapshot) {
5051
CharStream cs = CharStreams.fromString(String.valueOf(snapshot.getText()));
5152
ANTLRv3Lexer lexer = new ANTLRv3Lexer(cs);
5253
CommonTokenStream tokens = new CommonTokenStream(lexer);
53-
return new ANTLRv3Parser(tokens);
54+
ANTLRv3Parser ret = new ANTLRv3Parser(tokens);
55+
ret.removeErrorListener(ConsoleErrorListener.INSTANCE);
56+
return ret;
5457
}
5558

5659
@Override
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!--
3+
4+
Licensed to the Apache Software Foundation (ASF) under one
5+
or more contributor license agreements. See the NOTICE file
6+
distributed with this work for additional information
7+
regarding copyright ownership. The ASF licenses this file
8+
to you under the Apache License, Version 2.0 (the
9+
"License"); you may not use this file except in compliance
10+
with the License. You may obtain a copy of the License at
11+
12+
http://www.apache.org/licenses/LICENSE-2.0
13+
14+
Unless required by applicable law or agreed to in writing,
15+
software distributed under the License is distributed on an
16+
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17+
KIND, either express or implied. See the License for the
18+
specific language governing permissions and limitations
19+
under the License.
20+
21+
-->
22+
<!DOCTYPE editor-preferences PUBLIC "-//NetBeans//DTD Editor Preferences 1.0//EN" "http://www.netbeans.org/dtds/EditorPreferences-1_0.dtd">
23+
24+
<editor-preferences>
25+
<entry category="private" name="custom-action-list" value="org.netbeans.modules.languages.yaml.InsertTabAction.createCustomActions" />
26+
<entry javaType="java.lang.Boolean" name="enable-indent" xml:space="preserve"><value>true</value></entry>
27+
<entry javaType="java.lang.Integer" name="indent-shift-width" xml:space="preserve"><value>4</value></entry>
28+
</editor-preferences>

java/languages.antlr/src/org/netbeans/modules/languages/antlr/v4/Antlr4CompletionProvider.java

Lines changed: 7 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,6 @@
3333
import org.antlr.v4.runtime.CharStreams;
3434
import org.antlr.v4.runtime.Token;
3535
import org.netbeans.api.editor.document.EditorDocumentUtils;
36-
import org.netbeans.api.editor.document.LineDocument;
37-
import org.netbeans.api.editor.document.LineDocumentUtils;
3836
import org.netbeans.api.editor.mimelookup.MimeLookup;
3937
import org.netbeans.api.editor.mimelookup.MimePath;
4038
import org.netbeans.api.editor.mimelookup.MimeRegistration;
@@ -86,18 +84,6 @@ public AntlrCompletionQuery(boolean caseSensitive) {
8684
this.caseSensitive = caseSensitive;
8785
}
8886

89-
//TODO: This is a Lexer based pretty dumb implementation. Only offer
90-
// prefix if the cursor is at the end of a start of token/lexer rule.
91-
// Shall be replaced with a better approach.
92-
private String getPrefix(Document doc, int caretOffset, boolean upToOffset) throws BadLocationException {
93-
LineDocument lineDoc = LineDocumentUtils.asRequired(doc, LineDocument.class);
94-
int start = LineDocumentUtils.getWordStart(lineDoc, caretOffset);
95-
int end = LineDocumentUtils.getWordEnd(lineDoc, caretOffset);
96-
String prefix = doc.getText(start, (upToOffset ? caretOffset : end) - start);
97-
98-
return (prefix.length() > 0) && !Character.isWhitespace(prefix.codePointAt(prefix.length() - 1)) ? prefix : "";
99-
}
100-
10187
@Override
10288
protected void query(CompletionResultSet resultSet, Document doc, int caretOffset) {
10389
AbstractDocument adoc = (AbstractDocument) doc;
@@ -143,12 +129,10 @@ protected void query(CompletionResultSet resultSet, Document doc, int caretOffse
143129
tokens.previous();
144130
lookAround(fo, tokens, caretOffset, prefix, resultSet);
145131
} else {
146-
//Empty grammar so far offer lexer and grammar
147-
addTokens("", caretOffset, resultSet, "lexer", "grammar");
132+
//Empty grammar so far offer lexer, parser and grammar
133+
addTokens("", caretOffset, resultSet, "lexer", "parser", "grammar");
148134
}
149135
}
150-
} catch (Throwable th) {
151-
System.out.println(th);
152136
} finally {
153137
resultSet.finish();
154138
}
@@ -162,6 +146,9 @@ private void lookAround(FileObject fo, AntlrTokenSequence tokens, int caretOffse
162146
if (t.isPresent() && t.get().getType() != LEXER) {
163147
addTokens(prefix, caretOffset, resultSet, "lexer");
164148
}
149+
if (t.isPresent() && t.get().getType() != PARSER) {
150+
addTokens(prefix, caretOffset, resultSet, "parser");
151+
}
165152
if (t.isPresent() && (t.get().getType() != LEXER) && (t.get().getType() != GRAMMAR)) {
166153
addTokens(prefix, caretOffset, resultSet, "grammar");
167154
}
@@ -174,11 +161,12 @@ private void lookAround(FileObject fo, AntlrTokenSequence tokens, int caretOffse
174161
opt = tokens.previous(DEFAULT_CHANNEL);
175162
}
176163
if (!opt.isPresent()) {
177-
addTokens(prefix, caretOffset, resultSet, "lexer", "grammar");
164+
addTokens(prefix, caretOffset, resultSet, "lexer", "parser", "grammar");
178165
return;
179166
} else {
180167
pt = opt.get();
181168
switch (pt.getType()) {
169+
case PARSER:
182170
case LEXER:
183171
Optional<Token> t = tokens.next(DEFAULT_CHANNEL);
184172
if (!t.isPresent() || t.get().getType() != GRAMMAR) {
Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
package org.netbeans.modules.languages.antlr.v4;
20+
21+
import java.util.prefs.Preferences;
22+
import javax.swing.text.BadLocationException;
23+
import javax.swing.text.Document;
24+
import org.antlr.parser.antlr4.ANTLRv4Lexer;
25+
import org.antlr.v4.runtime.CharStreams;
26+
import org.netbeans.modules.csl.api.Formatter;
27+
import org.netbeans.modules.csl.spi.ParserResult;
28+
import org.netbeans.modules.editor.indent.spi.Context;
29+
30+
import static org.antlr.parser.antlr4.ANTLRv4Lexer.*;
31+
import org.antlr.v4.runtime.Token;
32+
import org.netbeans.api.editor.document.LineDocument;
33+
import org.netbeans.api.editor.document.LineDocumentUtils;
34+
import org.netbeans.api.editor.settings.SimpleValueNames;
35+
import org.netbeans.modules.editor.indent.spi.CodeStylePreferences;
36+
37+
/**
38+
*
39+
* @author lkishalmi
40+
*/
41+
public class Antlr4Formatter implements Formatter {
42+
43+
public Antlr4Formatter() {
44+
}
45+
46+
@Override
47+
public void reformat(Context context, ParserResult compilationInfo) {
48+
LineDocument doc = LineDocumentUtils.as(context.document(), LineDocument.class);
49+
String text;
50+
int indentSize = getIndentSize(context.document());
51+
try {
52+
text = doc.getText(0, doc.getLength());
53+
} catch (BadLocationException ex) {
54+
return;
55+
}
56+
57+
ANTLRv4Lexer lexer = new ANTLRv4Lexer(CharStreams.fromString(text));
58+
Token token = lexer.nextToken();
59+
if (token.getType() == EOF) {
60+
return;
61+
}
62+
63+
final int cstart = context.startOffset();
64+
final int cend = context.endOffset();
65+
66+
int tstart = token.getStartIndex();
67+
int tstop = token.getStopIndex();
68+
69+
try {
70+
boolean inRule = false;
71+
int ruleLine = 0;
72+
int parenDepth = 0;
73+
int textDelta = 0;
74+
int prevTokenType = -1;
75+
while ((token.getType() != EOF) && (tstart < cend)) {
76+
switch (token.getType()) {
77+
case RULE_REF:
78+
case TOKEN_REF:
79+
// @header , @member, etc, are not real rules;
80+
if (!inRule && (prevTokenType != AT)) {
81+
inRule = true;
82+
}
83+
break;
84+
case LPAREN:
85+
parenDepth++;
86+
break;
87+
case RPAREN:
88+
parenDepth--;
89+
break;
90+
case SEMI:
91+
inRule = false;
92+
break;
93+
}
94+
if (tstop >= cstart) {
95+
if (!context.isIndent()) {
96+
//TODO: Do non-indent formatting
97+
}
98+
if (token.getChannel() == OFF_CHANNEL) {
99+
String ttext = token.getText();
100+
int nl = ttext.indexOf('\n');
101+
while ((nl != -1) && (tstart + nl <= cend)) {
102+
int lineStart = context.lineStartOffset(tstart + textDelta + nl);
103+
if (inRule || ruleLine > 0) {
104+
if (tstart + nl >= cstart) {
105+
// Indent the first rule line to 0 the rest to indentSize
106+
int originalIndent = context.lineIndent(lineStart);
107+
int newIndent = ruleLine > 0 ? indentSize : 0;
108+
context.modifyIndent(lineStart, newIndent);
109+
textDelta += newIndent - originalIndent;
110+
}
111+
ruleLine = inRule ? ruleLine + 1 : 0;
112+
}
113+
nl = ttext.indexOf('\n', nl + 1);
114+
}
115+
}
116+
}
117+
prevTokenType = token.getType();
118+
token = lexer.nextToken();
119+
tstart = token.getStartIndex();
120+
tstop = token.getStopIndex();
121+
}
122+
} catch (BadLocationException ex) {}
123+
}
124+
125+
@Override
126+
public void reindent(Context context) {
127+
reformat(context, null);
128+
}
129+
130+
@Override
131+
public boolean needsParserResult() {
132+
return false;
133+
}
134+
135+
@Override
136+
public int indentSize() {
137+
return 4;
138+
}
139+
140+
@Override
141+
public int hangingIndentSize() {
142+
return 4;
143+
}
144+
145+
static int getIndentSize(Document doc) {
146+
Preferences prefs = CodeStylePreferences.get(doc).getPreferences();
147+
return prefs.getInt(SimpleValueNames.INDENT_SHIFT_WIDTH, 4);
148+
}
149+
}

java/languages.antlr/src/org/netbeans/modules/languages/antlr/v4/Antlr4Language.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import org.netbeans.core.spi.multiview.MultiViewElement;
2424
import org.netbeans.core.spi.multiview.text.MultiViewEditorElement;
2525
import org.netbeans.modules.csl.api.DeclarationFinder;
26+
import org.netbeans.modules.csl.api.Formatter;
2627
import org.netbeans.modules.csl.api.OccurrencesFinder;
2728
import org.netbeans.modules.csl.api.StructureScanner;
2829
import org.netbeans.modules.csl.spi.DefaultLanguageConfig;
@@ -131,6 +132,16 @@ public String getDisplayName() {
131132
return Bundle.ANTLRv4Resolver();
132133
}
133134

135+
@Override
136+
public Formatter getFormatter() {
137+
return new Antlr4Formatter();
138+
}
139+
140+
@Override
141+
public boolean hasFormatter() {
142+
return true;
143+
}
144+
134145
@Override
135146
public String getPreferredExtension() {
136147
return "g4";

java/languages.antlr/src/org/netbeans/modules/languages/antlr/v4/Antlr4ParserResult.java

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import org.antlr.v4.runtime.CharStream;
3232
import org.antlr.v4.runtime.CharStreams;
3333
import org.antlr.v4.runtime.CommonTokenStream;
34+
import org.antlr.v4.runtime.ConsoleErrorListener;
3435
import org.antlr.v4.runtime.Token;
3536
import org.antlr.v4.runtime.tree.ParseTreeListener;
3637
import org.antlr.v4.runtime.tree.TerminalNode;
@@ -64,7 +65,9 @@ protected ANTLRv4Parser createParser(Snapshot snapshot) {
6465
CharStream cs = CharStreams.fromString(String.valueOf(snapshot.getText()));
6566
ANTLRv4Lexer lexer = new org.antlr.parser.antlr4.ANTLRv4Lexer(cs);
6667
CommonTokenStream tokens = new CommonTokenStream(lexer);
67-
return new ANTLRv4Parser(tokens);
68+
ANTLRv4Parser ret = new ANTLRv4Parser(tokens);
69+
ret.removeErrorListener(ConsoleErrorListener.INSTANCE);
70+
return ret;
6871
}
6972

7073
@Override
@@ -82,14 +85,18 @@ protected ParseTreeListener createReferenceListener() {
8285
return new ANTLRv4ParserBaseListener() {
8386
@Override
8487
public void exitParserRuleSpec(ANTLRv4Parser.ParserRuleSpecContext ctx) {
85-
Token token = ctx.RULE_REF().getSymbol();
86-
addReference(token);
88+
if (ctx.RULE_REF() != null) {
89+
Token token = ctx.RULE_REF().getSymbol();
90+
addReference(token);
91+
}
8792
}
8893

8994
@Override
9095
public void exitLexerRuleSpec(ANTLRv4Parser.LexerRuleSpecContext ctx) {
91-
Token token = ctx.TOKEN_REF().getSymbol();
92-
addReference(token);
96+
if (ctx.TOKEN_REF() != null) {
97+
Token token = ctx.TOKEN_REF().getSymbol();
98+
addReference(token);
99+
}
93100
}
94101

95102
@Override
@@ -112,7 +119,9 @@ public void exitChannelsSpec(ANTLRv4Parser.ChannelsSpecContext ctx) {
112119

113120
@Override
114121
public void exitModeSpec(ANTLRv4Parser.ModeSpecContext ctx) {
115-
addReference(getIdentifierToken(ctx.identifier()));
122+
if (ctx.identifier() != null) {
123+
addReference(getIdentifierToken(ctx.identifier()));
124+
}
116125
}
117126

118127
public void addReference(Token token) {
@@ -311,7 +320,9 @@ public void exitRuleref(ANTLRv4Parser.RulerefContext ctx) {
311320

312321
@Override
313322
public void exitLexerCommandExpr(ANTLRv4Parser.LexerCommandExprContext ctx) {
314-
onOccurance.accept(getIdentifierToken(ctx.identifier()));
323+
if (ctx.identifier() != null) {
324+
onOccurance.accept(getIdentifierToken(ctx.identifier()));
325+
}
315326
}
316327

317328

0 commit comments

Comments
 (0)