This is an automated email from the ASF dual-hosted git repository. sreimers pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/incubator-netbeans.git
The following commit(s) were added to refs/heads/master by this push: new a9e5e74 [NETBEANS-1292] Adding simple infrastructure to write Groovy Hints based on AST (not errors). a9e5e74 is described below commit a9e5e745f7f83e0b930c956e54027537a229e07f Author: Sven Reimers <svenreim...@users.noreply.github.com> AuthorDate: Sun Sep 30 17:33:29 2018 +0200 [NETBEANS-1292] Adding simple infrastructure to write Groovy Hints based on AST (not errors). --- .../groovy/editor/hints/HintsAdvancedOption.java | 63 ++++++++ .../editor/hints/RemoveUnusedImportHint.java | 178 +++++++++++++++++++++ .../editor/hints/infrastructure/Bundle.properties | 18 +++ .../editor/hints/infrastructure/GroovyAstRule.java | 38 +++++ .../hints/infrastructure/GroovyHintsProvider.java | 37 ++++- .../modules/groovy/editor/resources/layer.xml | 19 +++ 6 files changed, 351 insertions(+), 2 deletions(-) diff --git a/groovy/groovy.editor/src/org/netbeans/modules/groovy/editor/hints/HintsAdvancedOption.java b/groovy/groovy.editor/src/org/netbeans/modules/groovy/editor/hints/HintsAdvancedOption.java new file mode 100644 index 0000000..5271ede --- /dev/null +++ b/groovy/groovy.editor/src/org/netbeans/modules/groovy/editor/hints/HintsAdvancedOption.java @@ -0,0 +1,63 @@ +/* + * 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.groovy.editor.hints; + +import org.netbeans.modules.csl.api.HintsProvider; +import org.netbeans.modules.groovy.editor.api.parser.GroovyLanguage; +import org.netbeans.spi.options.AdvancedOption; +import org.netbeans.spi.options.OptionsPanelController; +import org.openide.util.NbBundle; + +/** + * + * @author Sven Reimers + */ +public class HintsAdvancedOption extends AdvancedOption { + + OptionsPanelController panelController; + + @Override + @NbBundle.Messages("CTL_Hints_DisplayName=Hints") + public String getDisplayName() { + return Bundle.CTL_Hints_DisplayName(); + } + + @Override + @NbBundle.Messages("CTL_Hints_ToolTip=Static code verification for Groovy") + public String getTooltip() { + return Bundle.CTL_Hints_ToolTip(); + } + + @Override + public synchronized OptionsPanelController create() { + if ( panelController == null ) { + HintsProvider.HintsManager manager = HintsProvider.HintsManager.getManagerForMimeType(GroovyLanguage.GROOVY_MIME_TYPE); + assert manager != null; + panelController = manager.getOptionsController(); + } + + return panelController; + } + + //TODO: temporary solution, this should be solved on GSF level + public static OptionsPanelController createStatic(){ + return new HintsAdvancedOption().create(); + } +} diff --git a/groovy/groovy.editor/src/org/netbeans/modules/groovy/editor/hints/RemoveUnusedImportHint.java b/groovy/groovy.editor/src/org/netbeans/modules/groovy/editor/hints/RemoveUnusedImportHint.java new file mode 100644 index 0000000..1d16876 --- /dev/null +++ b/groovy/groovy.editor/src/org/netbeans/modules/groovy/editor/hints/RemoveUnusedImportHint.java @@ -0,0 +1,178 @@ +/* + * 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.groovy.editor.hints; + +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.prefs.Preferences; +import javax.swing.JComponent; +import javax.swing.text.BadLocationException; +import org.codehaus.groovy.ast.ImportNode; +import org.codehaus.groovy.ast.ModuleNode; +import org.netbeans.editor.BaseDocument; +import org.netbeans.editor.FinderFactory; +import org.netbeans.modules.csl.api.EditList; +import org.netbeans.modules.csl.api.Hint; +import org.netbeans.modules.csl.api.HintFix; +import org.netbeans.modules.csl.api.HintSeverity; +import org.netbeans.modules.csl.api.RuleContext; +import org.netbeans.modules.editor.NbEditorUtilities; +import org.netbeans.modules.groovy.editor.api.ASTUtils; +import org.netbeans.modules.groovy.editor.api.lexer.GroovyTokenId; +import org.netbeans.modules.groovy.editor.api.lexer.LexUtilities; +import org.netbeans.modules.groovy.editor.hints.infrastructure.GroovyAstRule; +import org.netbeans.modules.groovy.editor.hints.infrastructure.GroovyHintsProvider; +import org.openide.util.Exceptions; + +/** + * + * @author Sven Reimers + */ +public class RemoveUnusedImportHint extends GroovyAstRule { + + @Override + public void computeHints(GroovyHintsProvider.GroovyRuleContext context, List<Hint> result) { + final ModuleNode moduleNode = context.getGroovyParserResult().getRootElement().getModuleNode(); + if (null == moduleNode) { + return; + } + List<ImportNode> importNodes = moduleNode.getImports(); + for (ImportNode importNode : importNodes) { + + String alias = importNode.getAlias(); + + try { + int find = 0; + final FinderFactory.StringFwdFinder stringFwdFinder = new FinderFactory.StringFwdFinder(alias, true); + + while(-1 != (find = context.doc.find(stringFwdFinder, find+1, -1)) && skipUsage(find, context.doc)); + + if (-1 == find) { + result.add(new Hint(this, "Unused Import", + NbEditorUtilities.getFileObject(context.doc), + ASTUtils.getRangeFull(importNode, context.doc), + Collections.<HintFix>singletonList(new RemoveUnusedImportFix("Remove unused import", context.doc, importNode)), 1)); + } + } catch (BadLocationException ex) { + Exceptions.printStackTrace(ex); + } + } + } + + private boolean skipUsage(int position, BaseDocument doc) { + int lineNo = NbEditorUtilities.getLine(doc, position, true).getLineNumber(); + if (0 == lineNo) { + return true; + } + GroovyTokenId tokenIdAtPosition = LexUtilities.getToken(doc, position).id(); + return GroovyTokenId.LITERAL_import == + LexUtilities.getToken(doc, ASTUtils.getOffset(doc, lineNo+1, 2)).id() + || GroovyTokenId.SH_COMMENT == tokenIdAtPosition + || GroovyTokenId.SL_COMMENT == tokenIdAtPosition + || GroovyTokenId.LINE_COMMENT == tokenIdAtPosition + || GroovyTokenId.BLOCK_COMMENT == tokenIdAtPosition; + } + + @Override + public Set<?> getKinds() { + return new HashSet<>(Arrays.asList(new String[]{"Import Hints"})); + } + + @Override + public String getId() { + return "imports.unused.hint"; + } + + @Override + public String getDescription() { + return "Remove unused Import"; + } + + @Override + public boolean getDefaultEnabled() { + return true; + } + + @Override + public JComponent getCustomizer(Preferences node) { + return null; + } + + @Override + public boolean appliesTo(RuleContext context) { + return context instanceof GroovyHintsProvider.GroovyRuleContext; + } + + @Override + public String getDisplayName() { + return "Remove unused import"; + } + + @Override + public boolean showInTasklist() { + return false; + } + + @Override + public HintSeverity getDefaultSeverity() { + return HintSeverity.INFO; + } + + private static class RemoveUnusedImportFix implements HintFix { + + final BaseDocument baseDoc; + final String desc; + final ImportNode importNode; + + public RemoveUnusedImportFix(String desc, BaseDocument baseDoc, ImportNode importNode) { + this.desc = desc; + this.baseDoc = baseDoc; + this.importNode = importNode; + } + + @Override + public String getDescription() { + return desc; + } + + @Override + public void implement() throws Exception { + EditList edits = new EditList(baseDoc); + int offset = ASTUtils.getOffset(baseDoc,importNode.getLineNumber(), 1); + int removeLen = ASTUtils.getOffset(baseDoc,importNode.getLineNumber()+1, 1)-offset; + edits.replace(offset, removeLen, "", true, 0); + edits.apply(); + } + + @Override + public boolean isSafe() { + return false; + } + + @Override + public boolean isInteractive() { + return false; + } + } + +} diff --git a/groovy/groovy.editor/src/org/netbeans/modules/groovy/editor/hints/infrastructure/Bundle.properties b/groovy/groovy.editor/src/org/netbeans/modules/groovy/editor/hints/infrastructure/Bundle.properties new file mode 100644 index 0000000..88e9918 --- /dev/null +++ b/groovy/groovy.editor/src/org/netbeans/modules/groovy/editor/hints/infrastructure/Bundle.properties @@ -0,0 +1,18 @@ +# 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. + +csl-hints/text/x-groovy/hints/import=Import \ No newline at end of file diff --git a/groovy/groovy.editor/src/org/netbeans/modules/groovy/editor/hints/infrastructure/GroovyAstRule.java b/groovy/groovy.editor/src/org/netbeans/modules/groovy/editor/hints/infrastructure/GroovyAstRule.java new file mode 100644 index 0000000..db2650e --- /dev/null +++ b/groovy/groovy.editor/src/org/netbeans/modules/groovy/editor/hints/infrastructure/GroovyAstRule.java @@ -0,0 +1,38 @@ +/* + * 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.groovy.editor.hints.infrastructure; + +import java.util.List; +import java.util.Set; +import org.netbeans.modules.csl.api.Hint; +import org.netbeans.modules.csl.api.Rule.AstRule; + +/** + * Represents a rule to be run on a Groovy AstNode + * + * @author Sven Reimers + */ +public abstract class GroovyAstRule implements AstRule { + + public abstract void computeHints(GroovyHintsProvider.GroovyRuleContext context, List<Hint> result); + + public abstract Set<?> getKinds(); + +} diff --git a/groovy/groovy.editor/src/org/netbeans/modules/groovy/editor/hints/infrastructure/GroovyHintsProvider.java b/groovy/groovy.editor/src/org/netbeans/modules/groovy/editor/hints/infrastructure/GroovyHintsProvider.java index 6becd1d..a576b82 100644 --- a/groovy/groovy.editor/src/org/netbeans/modules/groovy/editor/hints/infrastructure/GroovyHintsProvider.java +++ b/groovy/groovy.editor/src/org/netbeans/modules/groovy/editor/hints/infrastructure/GroovyHintsProvider.java @@ -23,6 +23,8 @@ import java.util.logging.Level; import java.util.logging.Logger; import java.util.List; import java.util.Map; +import java.util.function.Consumer; +import javax.swing.text.BadLocationException; import org.codehaus.groovy.ast.ASTNode; import org.netbeans.modules.csl.api.Error; import org.netbeans.modules.csl.api.Hint; @@ -43,15 +45,28 @@ import org.netbeans.modules.groovy.editor.compiler.error.GroovyError; public class GroovyHintsProvider implements HintsProvider { public static final Logger LOG = Logger.getLogger(GroovyHintsProvider.class.getName()); // NOI18N - private boolean cancelled; + private volatile boolean cancelled; @Override public RuleContext createRuleContext() { - return new RuleContext(); + return new GroovyRuleContext(); } @Override public void computeHints(HintsManager manager, RuleContext context, List<Hint> hints) { + Map<?, List<? extends Rule.AstRule>> allHints = manager.getHints(false, context); + for (Map.Entry<?,List<? extends Rule.AstRule>> hintsEntry : allHints.entrySet()) { + for (Rule.AstRule rule : hintsEntry.getValue()) { + if (rule instanceof GroovyAstRule) { + ((GroovyAstRule)rule).computeHints((GroovyRuleContext)context, hints); + } + } + + } + } + + private void invokeHint(GroovyAstRule rule, HintsManager manager, RuleContext context, List<Hint> hints) { + rule.computeHints((GroovyRuleContext) context, hints); } @Override @@ -139,6 +154,7 @@ public class GroovyHintsProvider implements HintsProvider { @Override public void cancel() { + cancelled = true; } @Override @@ -189,4 +205,21 @@ public class GroovyHintsProvider implements HintsProvider { return false; } + + public class GroovyRuleContext extends RuleContext { + + private GroovyParserResult groovyParserResult = null; + + public GroovyParserResult getGroovyParserResult() { + if (groovyParserResult == null) { + groovyParserResult = (GroovyParserResult)parserResult; + } + return groovyParserResult; + } + + public boolean isCancelled() { + return cancelled; + } + + } } diff --git a/groovy/groovy.editor/src/org/netbeans/modules/groovy/editor/resources/layer.xml b/groovy/groovy.editor/src/org/netbeans/modules/groovy/editor/resources/layer.xml index 7d52fb3..613f0d5 100644 --- a/groovy/groovy.editor/src/org/netbeans/modules/groovy/editor/resources/layer.xml +++ b/groovy/groovy.editor/src/org/netbeans/modules/groovy/editor/resources/layer.xml @@ -33,6 +33,12 @@ <folder name="csl-hints"> <folder name="text"> <folder name="x-groovy"> + <folder name="hints"> + <folder name="import"> + <attr name="SystemFileSystem.localizingBundle" stringvalue="org.netbeans.modules.groovy.editor.hints.infrastructure.Bundle"/> + <file name="org-netbeans-modules-groovy-editor-hints-RemoveUnusedImportHint.instance"/> + </folder> + </folder> <folder name="selection"> <file name="org-netbeans-modules-groovy-editor-hints-SurroundWithHint.instance"/> </folder> @@ -115,6 +121,19 @@ <file name="x-groovy" url="FontsAndColorsPreview.groovy"/> </folder> </folder> + <folder name="Editor"> + <folder name="Hints"> + <attr name="position" intvalue="0"/> + <folder name="text"> + <folder name="x-groovy"> + <file name="GroovyHints.instance"> + <attr name="instanceOf" stringvalue="org.netbeans.spi.options.OptionsPanelController"/> + <attr name="instanceCreate" methodvalue="org.netbeans.modules.groovy.editor.hints.HintsAdvancedOption.createStatic"/> + </file> + </folder> + </folder> + </folder> + </folder> </folder> <folder name="Loaders"> --------------------------------------------------------------------- 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