You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@netbeans.apache.org by sr...@apache.org on 2018/09/30 15:33:33 UTC

[incubator-netbeans] branch master updated: [NETBEANS-1292] Adding simple infrastructure to write Groovy Hints based on AST (not errors).

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 <sv...@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-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