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 abb1a97c84 ANTLRv4 indent and code snippet support. (#4800) abb1a97c84 is described below commit abb1a97c84e22da99cd38640d52f57e029fa2dbb Author: Laszlo Kishalmi <laszlo.kisha...@gmail.com> AuthorDate: Mon Oct 17 10:44:02 2022 -0700 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 --- .../modules/languages/antlr/AntlrParserResult.java | 1 - .../org/netbeans/modules/languages/antlr/layer.xml | 13 ++ .../languages/antlr/v3/Antlr3ParserResult.java | 5 +- .../modules/languages/antlr/v3/preferences.xml | 28 ++++ .../antlr/v4/Antlr4CompletionProvider.java | 26 +--- .../languages/antlr/v4/Antlr4Formatter.java | 149 +++++++++++++++++++++ .../modules/languages/antlr/v4/Antlr4Language.java | 11 ++ .../languages/antlr/v4/Antlr4ParserResult.java | 25 +++- .../modules/languages/antlr/v4/preferences.xml | 28 ++++ .../modules/languages/antlr/v4/snippets.xml | 56 ++++++++ 10 files changed, 314 insertions(+), 28 deletions(-) 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 eaaae99e85..009e288dd6 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 @@ -64,7 +64,6 @@ public abstract class AntlrParserResult<T extends Parser> extends ParserResult { public AntlrParserResult get() { if (! finished) { - FileObject fo = getSnapshot().getSource().getFileObject(); T parser = createParser(getSnapshot()); parser.addErrorListener(createErrorListener()); parser.addParseListener(createFoldListener()); diff --git a/java/languages.antlr/src/org/netbeans/modules/languages/antlr/layer.xml b/java/languages.antlr/src/org/netbeans/modules/languages/antlr/layer.xml index 0972a92bfa..d956ef1f30 100644 --- a/java/languages.antlr/src/org/netbeans/modules/languages/antlr/layer.xml +++ b/java/languages.antlr/src/org/netbeans/modules/languages/antlr/layer.xml @@ -41,6 +41,11 @@ </folder> </folder> </folder> + <folder name="Preferences"> + <folder name="Defaults"> + <file name="org-netbeans-modules-languages-antlr-v3-preferences.xml" url="v3/preferences.xml"/> + </folder> + </folder> </folder> <folder name="x-antlr4"> <attr name="displayName" bundlevalue="org.netbeans.modules.languages.antlr.Bundle#Editors/text/x-antlr4"/> @@ -60,6 +65,14 @@ </folder> </folder> </folder> + <folder name="CodeTemplates"> + <file name="org-netbeans-modules-languages-antlr-v4-snippets.xml" url="v4/snippets.xml"/> + </folder> + <folder name="Preferences"> + <folder name="Defaults"> + <file name="org-netbeans-modules-languages-antlr-v4-preferences.xml" url="v4/preferences.xml"/> + </folder> + </folder> </folder> </folder> </folder> 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 74b325bef8..81e01bbc28 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 @@ -26,6 +26,7 @@ import org.antlr.parser.antlr3.ANTLRv3ParserBaseListener; import org.antlr.v4.runtime.CharStream; import org.antlr.v4.runtime.CharStreams; import org.antlr.v4.runtime.CommonTokenStream; +import org.antlr.v4.runtime.ConsoleErrorListener; import org.antlr.v4.runtime.Token; import org.antlr.v4.runtime.tree.ParseTreeListener; import org.netbeans.modules.csl.api.OffsetRange; @@ -50,7 +51,9 @@ public final class Antlr3ParserResult extends AntlrParserResult<ANTLRv3Parser> { CharStream cs = CharStreams.fromString(String.valueOf(snapshot.getText())); ANTLRv3Lexer lexer = new ANTLRv3Lexer(cs); CommonTokenStream tokens = new CommonTokenStream(lexer); - return new ANTLRv3Parser(tokens); + ANTLRv3Parser ret = new ANTLRv3Parser(tokens); + ret.removeErrorListener(ConsoleErrorListener.INSTANCE); + return ret; } @Override diff --git a/java/languages.antlr/src/org/netbeans/modules/languages/antlr/v3/preferences.xml b/java/languages.antlr/src/org/netbeans/modules/languages/antlr/v3/preferences.xml new file mode 100644 index 0000000000..29d9676ecb --- /dev/null +++ b/java/languages.antlr/src/org/netbeans/modules/languages/antlr/v3/preferences.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + + 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. + +--> +<!DOCTYPE editor-preferences PUBLIC "-//NetBeans//DTD Editor Preferences 1.0//EN" "http://www.netbeans.org/dtds/EditorPreferences-1_0.dtd"> + +<editor-preferences> + <entry category="private" name="custom-action-list" value="org.netbeans.modules.languages.yaml.InsertTabAction.createCustomActions" /> + <entry javaType="java.lang.Boolean" name="enable-indent" xml:space="preserve"><value>true</value></entry> + <entry javaType="java.lang.Integer" name="indent-shift-width" xml:space="preserve"><value>4</value></entry> +</editor-preferences> 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 index b072a74d02..a2a3a985ea 100644 --- 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 @@ -33,8 +33,6 @@ 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; @@ -86,18 +84,6 @@ public class Antlr4CompletionProvider implements CompletionProvider { 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; @@ -143,12 +129,10 @@ public class Antlr4CompletionProvider implements CompletionProvider { tokens.previous(); lookAround(fo, tokens, caretOffset, prefix, resultSet); } else { - //Empty grammar so far offer lexer and grammar - addTokens("", caretOffset, resultSet, "lexer", "grammar"); + //Empty grammar so far offer lexer, parser and grammar + addTokens("", caretOffset, resultSet, "lexer", "parser", "grammar"); } } - } catch (Throwable th) { - System.out.println(th); } finally { resultSet.finish(); } @@ -162,6 +146,9 @@ public class Antlr4CompletionProvider implements CompletionProvider { if (t.isPresent() && t.get().getType() != LEXER) { addTokens(prefix, caretOffset, resultSet, "lexer"); } + if (t.isPresent() && t.get().getType() != PARSER) { + addTokens(prefix, caretOffset, resultSet, "parser"); + } if (t.isPresent() && (t.get().getType() != LEXER) && (t.get().getType() != GRAMMAR)) { addTokens(prefix, caretOffset, resultSet, "grammar"); } @@ -174,11 +161,12 @@ public class Antlr4CompletionProvider implements CompletionProvider { opt = tokens.previous(DEFAULT_CHANNEL); } if (!opt.isPresent()) { - addTokens(prefix, caretOffset, resultSet, "lexer", "grammar"); + addTokens(prefix, caretOffset, resultSet, "lexer", "parser", "grammar"); return; } else { pt = opt.get(); switch (pt.getType()) { + case PARSER: case LEXER: Optional<Token> t = tokens.next(DEFAULT_CHANNEL); if (!t.isPresent() || t.get().getType() != GRAMMAR) { diff --git a/java/languages.antlr/src/org/netbeans/modules/languages/antlr/v4/Antlr4Formatter.java b/java/languages.antlr/src/org/netbeans/modules/languages/antlr/v4/Antlr4Formatter.java new file mode 100644 index 0000000000..afc7d0e63e --- /dev/null +++ b/java/languages.antlr/src/org/netbeans/modules/languages/antlr/v4/Antlr4Formatter.java @@ -0,0 +1,149 @@ +/* + * 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.prefs.Preferences; +import javax.swing.text.BadLocationException; +import javax.swing.text.Document; +import org.antlr.parser.antlr4.ANTLRv4Lexer; +import org.antlr.v4.runtime.CharStreams; +import org.netbeans.modules.csl.api.Formatter; +import org.netbeans.modules.csl.spi.ParserResult; +import org.netbeans.modules.editor.indent.spi.Context; + +import static org.antlr.parser.antlr4.ANTLRv4Lexer.*; +import org.antlr.v4.runtime.Token; +import org.netbeans.api.editor.document.LineDocument; +import org.netbeans.api.editor.document.LineDocumentUtils; +import org.netbeans.api.editor.settings.SimpleValueNames; +import org.netbeans.modules.editor.indent.spi.CodeStylePreferences; + +/** + * + * @author lkishalmi + */ +public class Antlr4Formatter implements Formatter { + + public Antlr4Formatter() { + } + + @Override + public void reformat(Context context, ParserResult compilationInfo) { + LineDocument doc = LineDocumentUtils.as(context.document(), LineDocument.class); + String text; + int indentSize = getIndentSize(context.document()); + try { + text = doc.getText(0, doc.getLength()); + } catch (BadLocationException ex) { + return; + } + + ANTLRv4Lexer lexer = new ANTLRv4Lexer(CharStreams.fromString(text)); + Token token = lexer.nextToken(); + if (token.getType() == EOF) { + return; + } + + final int cstart = context.startOffset(); + final int cend = context.endOffset(); + + int tstart = token.getStartIndex(); + int tstop = token.getStopIndex(); + + try { + boolean inRule = false; + int ruleLine = 0; + int parenDepth = 0; + int textDelta = 0; + int prevTokenType = -1; + while ((token.getType() != EOF) && (tstart < cend)) { + switch (token.getType()) { + case RULE_REF: + case TOKEN_REF: + // @header , @member, etc, are not real rules; + if (!inRule && (prevTokenType != AT)) { + inRule = true; + } + break; + case LPAREN: + parenDepth++; + break; + case RPAREN: + parenDepth--; + break; + case SEMI: + inRule = false; + break; + } + if (tstop >= cstart) { + if (!context.isIndent()) { + //TODO: Do non-indent formatting + } + if (token.getChannel() == OFF_CHANNEL) { + String ttext = token.getText(); + int nl = ttext.indexOf('\n'); + while ((nl != -1) && (tstart + nl <= cend)) { + int lineStart = context.lineStartOffset(tstart + textDelta + nl); + if (inRule || ruleLine > 0) { + if (tstart + nl >= cstart) { + // Indent the first rule line to 0 the rest to indentSize + int originalIndent = context.lineIndent(lineStart); + int newIndent = ruleLine > 0 ? indentSize : 0; + context.modifyIndent(lineStart, newIndent); + textDelta += newIndent - originalIndent; + } + ruleLine = inRule ? ruleLine + 1 : 0; + } + nl = ttext.indexOf('\n', nl + 1); + } + } + } + prevTokenType = token.getType(); + token = lexer.nextToken(); + tstart = token.getStartIndex(); + tstop = token.getStopIndex(); + } + } catch (BadLocationException ex) {} + } + + @Override + public void reindent(Context context) { + reformat(context, null); + } + + @Override + public boolean needsParserResult() { + return false; + } + + @Override + public int indentSize() { + return 4; + } + + @Override + public int hangingIndentSize() { + return 4; + } + + static int getIndentSize(Document doc) { + Preferences prefs = CodeStylePreferences.get(doc).getPreferences(); + return prefs.getInt(SimpleValueNames.INDENT_SHIFT_WIDTH, 4); + } +} 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 d411060999..88fa824a6a 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 @@ -23,6 +23,7 @@ import org.netbeans.api.lexer.Language; import org.netbeans.core.spi.multiview.MultiViewElement; import org.netbeans.core.spi.multiview.text.MultiViewEditorElement; import org.netbeans.modules.csl.api.DeclarationFinder; +import org.netbeans.modules.csl.api.Formatter; import org.netbeans.modules.csl.api.OccurrencesFinder; import org.netbeans.modules.csl.api.StructureScanner; import org.netbeans.modules.csl.spi.DefaultLanguageConfig; @@ -131,6 +132,16 @@ public final class Antlr4Language extends DefaultLanguageConfig { return Bundle.ANTLRv4Resolver(); } + @Override + public Formatter getFormatter() { + return new Antlr4Formatter(); + } + + @Override + public boolean hasFormatter() { + return true; + } + @Override public String getPreferredExtension() { return "g4"; 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 b6823cc56c..e586a69ef2 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 @@ -31,6 +31,7 @@ import org.antlr.parser.antlr4.ANTLRv4ParserBaseListener; import org.antlr.v4.runtime.CharStream; import org.antlr.v4.runtime.CharStreams; import org.antlr.v4.runtime.CommonTokenStream; +import org.antlr.v4.runtime.ConsoleErrorListener; import org.antlr.v4.runtime.Token; import org.antlr.v4.runtime.tree.ParseTreeListener; import org.antlr.v4.runtime.tree.TerminalNode; @@ -64,7 +65,9 @@ public final class Antlr4ParserResult extends AntlrParserResult<ANTLRv4Parser> { CharStream cs = CharStreams.fromString(String.valueOf(snapshot.getText())); ANTLRv4Lexer lexer = new org.antlr.parser.antlr4.ANTLRv4Lexer(cs); CommonTokenStream tokens = new CommonTokenStream(lexer); - return new ANTLRv4Parser(tokens); + ANTLRv4Parser ret = new ANTLRv4Parser(tokens); + ret.removeErrorListener(ConsoleErrorListener.INSTANCE); + return ret; } @Override @@ -82,14 +85,18 @@ public final class Antlr4ParserResult extends AntlrParserResult<ANTLRv4Parser> { return new ANTLRv4ParserBaseListener() { @Override public void exitParserRuleSpec(ANTLRv4Parser.ParserRuleSpecContext ctx) { - Token token = ctx.RULE_REF().getSymbol(); - addReference(token); + if (ctx.RULE_REF() != null) { + Token token = ctx.RULE_REF().getSymbol(); + addReference(token); + } } @Override public void exitLexerRuleSpec(ANTLRv4Parser.LexerRuleSpecContext ctx) { - Token token = ctx.TOKEN_REF().getSymbol(); - addReference(token); + if (ctx.TOKEN_REF() != null) { + Token token = ctx.TOKEN_REF().getSymbol(); + addReference(token); + } } @Override @@ -112,7 +119,9 @@ public final class Antlr4ParserResult extends AntlrParserResult<ANTLRv4Parser> { @Override public void exitModeSpec(ANTLRv4Parser.ModeSpecContext ctx) { - addReference(getIdentifierToken(ctx.identifier())); + if (ctx.identifier() != null) { + addReference(getIdentifierToken(ctx.identifier())); + } } public void addReference(Token token) { @@ -311,7 +320,9 @@ public final class Antlr4ParserResult extends AntlrParserResult<ANTLRv4Parser> { @Override public void exitLexerCommandExpr(ANTLRv4Parser.LexerCommandExprContext ctx) { - onOccurance.accept(getIdentifierToken(ctx.identifier())); + if (ctx.identifier() != null) { + onOccurance.accept(getIdentifierToken(ctx.identifier())); + } } diff --git a/java/languages.antlr/src/org/netbeans/modules/languages/antlr/v4/preferences.xml b/java/languages.antlr/src/org/netbeans/modules/languages/antlr/v4/preferences.xml new file mode 100644 index 0000000000..29d9676ecb --- /dev/null +++ b/java/languages.antlr/src/org/netbeans/modules/languages/antlr/v4/preferences.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + + 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. + +--> +<!DOCTYPE editor-preferences PUBLIC "-//NetBeans//DTD Editor Preferences 1.0//EN" "http://www.netbeans.org/dtds/EditorPreferences-1_0.dtd"> + +<editor-preferences> + <entry category="private" name="custom-action-list" value="org.netbeans.modules.languages.yaml.InsertTabAction.createCustomActions" /> + <entry javaType="java.lang.Boolean" name="enable-indent" xml:space="preserve"><value>true</value></entry> + <entry javaType="java.lang.Integer" name="indent-shift-width" xml:space="preserve"><value>4</value></entry> +</editor-preferences> diff --git a/java/languages.antlr/src/org/netbeans/modules/languages/antlr/v4/snippets.xml b/java/languages.antlr/src/org/netbeans/modules/languages/antlr/v4/snippets.xml new file mode 100644 index 0000000000..8f8435dd13 --- /dev/null +++ b/java/languages.antlr/src/org/netbeans/modules/languages/antlr/v4/snippets.xml @@ -0,0 +1,56 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + + 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. + +--> + +<!DOCTYPE codetemplates PUBLIC "-//NetBeans//DTD Editor Code Templates settings 1.0//EN" "https://netbeans.apache.org/dtds/EditorCodeTemplates-1_0.dtd"> +<codetemplates> + <codetemplate abbreviation="ch" xml:space="preserve"> + <code>-> channel (${cursor})</code> + </codetemplate> + <codetemplate abbreviation="fr" xml:space="preserve"> + <code>fragment </code> + </codetemplate> + <codetemplate abbreviation="gr" xml:space="preserve"> + <code>grammar </code> + </codetemplate> + <codetemplate abbreviation="lx" xml:space="preserve"> + <code>lexer </code> + </codetemplate> + <codetemplate abbreviation="md" xml:space="preserve"> + <code>mode </code> + </codetemplate> + <codetemplate abbreviation="pa" xml:space="preserve"> + <code>parser </code> + </codetemplate> + <codetemplate abbreviation="pom" xml:space="preserve"> + <code>-> popMode</code> + </codetemplate> + <codetemplate abbreviation="pum" xml:space="preserve"> + <code>-> pushMode (${cursor})</code> + </codetemplate> + <codetemplate abbreviation="sk" xml:space="preserve"> + <code>-> skip</code> + </codetemplate> + <codetemplate abbreviation="tp" xml:space="preserve"> + <code>-> type (${cursor})</code> + </codetemplate> +</codetemplates> + --------------------------------------------------------------------- To unsubscribe, e-mail: commits-unsubscr...@netbeans.apache.org For additional commands, e-mail: commits-h...@netbeans.apache.org For further information about the NetBeans mailing lists, visit: https://cwiki.apache.org/confluence/display/NETBEANS/Mailing+lists