PY-20033 New EP that allows to suppress individual PEP 8 errors in files
authorMikhail Golubev <mikhail.golubev@jetbrains.com>
Mon, 11 Jul 2016 14:58:27 +0000 (17:58 +0300)
committerMikhail Golubev <mikhail.golubev@jetbrains.com>
Thu, 11 Aug 2016 12:05:03 +0000 (15:05 +0300)
I've used it to create extension that mutes W292
(no new line at end of file) in IPython notebook cells.

python/ipnb/resources/META-INF/ipython-notebook.xml
python/ipnb/src/org/jetbrains/plugins/ipnb/IpnbPep8ProblemSuppressor.java [new file with mode: 0644]
python/src/META-INF/python-core-common.xml
python/src/com/jetbrains/python/validation/Pep8ExternalAnnotator.java
python/src/com/jetbrains/python/validation/Pep8ProblemSuppressor.java [new file with mode: 0644]

index 7a2fb65cc1ca4c4cb26db62eace795a438f67d0c..324ef88f4e7676799a46e3d45ceb0dbe0e9c68f1 100644 (file)
@@ -18,6 +18,7 @@
   <extensions defaultExtensionNs="Pythonid">
     <dialectsTokenSetContributor implementation="org.jetbrains.plugins.ipnb.psi.IpnbPyTokenSetContributor"/>
     <visitorFilter language="IpnbPython" implementationClass="org.jetbrains.plugins.ipnb.IpnbVisitorFilter"/>
   <extensions defaultExtensionNs="Pythonid">
     <dialectsTokenSetContributor implementation="org.jetbrains.plugins.ipnb.psi.IpnbPyTokenSetContributor"/>
     <visitorFilter language="IpnbPython" implementationClass="org.jetbrains.plugins.ipnb.IpnbVisitorFilter"/>
+    <pep8ProblemSuppressor implementation="org.jetbrains.plugins.ipnb.IpnbPep8ProblemSuppressor"/>
   </extensions>
   <actions>
     <action class="org.jetbrains.plugins.ipnb.editor.actions.IpnbRunCellInplaceAction" id="IpnbRunCellInplaceAction"
   </extensions>
   <actions>
     <action class="org.jetbrains.plugins.ipnb.editor.actions.IpnbRunCellInplaceAction" id="IpnbRunCellInplaceAction"
diff --git a/python/ipnb/src/org/jetbrains/plugins/ipnb/IpnbPep8ProblemSuppressor.java b/python/ipnb/src/org/jetbrains/plugins/ipnb/IpnbPep8ProblemSuppressor.java
new file mode 100644 (file)
index 0000000..4b68d72
--- /dev/null
@@ -0,0 +1,22 @@
+package org.jetbrains.plugins.ipnb;
+
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiFile;
+import com.jetbrains.python.validation.Pep8ExternalAnnotator;
+import com.jetbrains.python.validation.Pep8ProblemSuppressor;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.jetbrains.plugins.ipnb.psi.IpnbPyFragment;
+
+/**
+ * @author Mikhail Golubev
+ */
+public class IpnbPep8ProblemSuppressor implements Pep8ProblemSuppressor {
+  @Override
+  public boolean isProblemSuppressed(@NotNull Pep8ExternalAnnotator.Problem problem,
+                                     @NotNull PsiFile file,
+                                     @Nullable PsiElement targetElement) {
+    // Ignore warnings about missing new line at the end of file inside IPython notebook cells
+    return file instanceof IpnbPyFragment && problem.getCode().equals("W292");
+  }
+}
index 29665e00b4df973c34c87e80fc75cdd1f9c2b074..28636cc51b6dc21d509152141c276658af6816f6 100644 (file)
     <extensionPoint qualifiedName="Pythonid.pyRootTypeProvider" interface="com.jetbrains.python.module.PyRootTypeProvider"/>
     <extensionPoint qualifiedName="Pythonid.runConfigurationEditorExtension" interface="com.jetbrains.python.run.PyRunConfigurationEditorExtension"/>
     <extensionPoint qualifiedName="Pythonid.pyCustomSdkUiProvider" interface="com.jetbrains.python.sdk.PyCustomSdkUiProvider"/>
     <extensionPoint qualifiedName="Pythonid.pyRootTypeProvider" interface="com.jetbrains.python.module.PyRootTypeProvider"/>
     <extensionPoint qualifiedName="Pythonid.runConfigurationEditorExtension" interface="com.jetbrains.python.run.PyRunConfigurationEditorExtension"/>
     <extensionPoint qualifiedName="Pythonid.pyCustomSdkUiProvider" interface="com.jetbrains.python.sdk.PyCustomSdkUiProvider"/>
+    <extensionPoint qualifiedName="Pythonid.pep8ProblemSuppressor" interface="com.jetbrains.python.validation.Pep8ProblemSuppressor"/>
   </extensionPoints>
 
   <extensions defaultExtensionNs="Pythonid">
   </extensionPoints>
 
   <extensions defaultExtensionNs="Pythonid">
index 529d0121599b4fba9e52ca249b2fe71de2bb6abc..af08f42538bd88e556bf42a2e2d144ebc790ff29 100644 (file)
@@ -68,6 +68,7 @@ import org.jetbrains.annotations.Nullable;
 
 import java.io.File;
 import java.util.ArrayList;
 
 import java.io.File;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
 import java.util.regex.Matcher;
 import java.util.Collections;
 import java.util.List;
 import java.util.regex.Matcher;
@@ -88,12 +89,30 @@ public class Pep8ExternalAnnotator extends ExternalAnnotator<Pep8ExternalAnnotat
     private final String myCode;
     private final String myDescription;
 
     private final String myCode;
     private final String myDescription;
 
-    public Problem(int line, int column, String code, String description) {
+    public Problem(int line, int column, @NotNull String code, @NotNull String description) {
       myLine = line;
       myColumn = column;
       myCode = code;
       myDescription = description;
     }
       myLine = line;
       myColumn = column;
       myCode = code;
       myDescription = description;
     }
+
+    public int getLine() {
+      return myLine;
+    }
+
+    public int getColumn() {
+      return myColumn;
+    }
+
+    @NotNull
+    public String getCode() {
+      return myCode;
+    }
+
+    @NotNull
+    public String getDescription() {
+      return myDescription;
+    }
   }
 
   public static class State {
   }
 
   public static class State {
@@ -247,7 +266,7 @@ public class Pep8ExternalAnnotator extends ExternalAnnotator<Pep8ExternalAnnotat
         problemElement = file.findElementAt(Math.max(0, offset - 1));
       }
 
         problemElement = file.findElementAt(Math.max(0, offset - 1));
       }
 
-      if (ignoreDueToSettings(project, problem, problemElement)) {
+      if (ignoreDueToSettings(project, problem, problemElement) || ignoredDueToProblemSuppressors(project, problem, file, problemElement)) {
         continue;
       }
 
         continue;
       }
 
@@ -297,6 +316,14 @@ public class Pep8ExternalAnnotator extends ExternalAnnotator<Pep8ExternalAnnotat
     }
   }
 
     }
   }
 
+  private static boolean ignoredDueToProblemSuppressors(@NotNull Project project,
+                                                        @NotNull Problem problem,
+                                                        @NotNull PsiFile file,
+                                                        @Nullable PsiElement element) {
+    final Pep8ProblemSuppressor[] suppressors = Pep8ProblemSuppressor.EP_NAME.getExtensions();
+    return Arrays.stream(suppressors).anyMatch(p -> p.isProblemSuppressed(problem, file, element));
+  }
+
   private static boolean crossesLineBoundary(@Nullable Document document, String text, TextRange problemRange) {
     int start = problemRange.getStartOffset();
     int end = problemRange.getEndOffset();
   private static boolean crossesLineBoundary(@Nullable Document document, String text, TextRange problemRange) {
     int start = problemRange.getStartOffset();
     int end = problemRange.getEndOffset();
diff --git a/python/src/com/jetbrains/python/validation/Pep8ProblemSuppressor.java b/python/src/com/jetbrains/python/validation/Pep8ProblemSuppressor.java
new file mode 100644 (file)
index 0000000..d181391
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2000-2016 JetBrains s.r.o.
+ *
+ * Licensed 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 com.jetbrains.python.validation;
+
+import com.intellij.openapi.extensions.ExtensionPointName;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiFile;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Custom filter that allows to selectively suppress warnings and errors produced by pycodestyle.py (former pep8.py).
+ * Note that by using {@link com.jetbrains.python.inspections.PythonVisitorFilter} you can disable PEP 8 inspection for concrete files
+ * altogether.
+ *
+ * @author Mikhail Golubev
+ * @see Pep8ExternalAnnotator
+ * @see com.jetbrains.python.inspections.PythonVisitorFilter
+ */
+public interface Pep8ProblemSuppressor {
+  ExtensionPointName<Pep8ProblemSuppressor> EP_NAME = ExtensionPointName.create("Pythonid.pep8ProblemSuppressor");
+
+  /**
+   * @param problem       a single problem returned by the script and extracted from its output
+   * @param file          PSI file where the inspection operates
+   * @param targetElement PSI element found in the place of a problem on which annotation is going be attached in the editor
+   * @return whether notification about this problem should be hidden in the editor
+   */
+  boolean isProblemSuppressed(@NotNull Pep8ExternalAnnotator.Problem problem,
+                              @NotNull PsiFile file,
+                              @Nullable PsiElement targetElement);
+}