PY-33235 Add highlighting annotator for nested functions
authorandrey.matveev <andrey.matveev@jetbrains.com>
Fri, 19 Jun 2020 12:09:32 +0000 (19:09 +0700)
committerintellij-monorepo-bot <intellij-monorepo-bot-no-reply@jetbrains.com>
Thu, 13 Aug 2020 17:37:39 +0000 (17:37 +0000)
GitOrigin-RevId: 0329f4af71bb86128531ab92c581a407339e1c7d

python/pluginResources/messages/PyBundle.properties
python/src/com/jetbrains/python/highlighting/PyHighlighter.java
python/src/com/jetbrains/python/highlighting/PythonColorsPage.java
python/src/com/jetbrains/python/validation/PyDefinitionsAnnotator.java
python/testData/highlighting/async.py
python/testData/highlighting/nestedFunction.py [new file with mode: 0644]
python/testData/highlighting/yieldInNestedFunction.py
python/testSrc/com/jetbrains/python/PythonHighlightingTest.java

index 5fa88502f91158ad5d888e108fdb8917119cede3..17da9e244235f6d08797c11bf7baa685bf0bffaa 100644 (file)
@@ -833,6 +833,7 @@ python.colors.keyword.argument=Keyword argument
 python.colors.parameters.self.parameter=Parameters//'self' parameter
 python.colors.parameters.parameter=Parameters//Parameter
 python.colors.functions.method.call=Functions//Method call
+python.colors.functions.nested.function.definition=Functions//Nested function definition
 python.colors.functions.function.call=Functions//Function call
 python.colors.functions.function.definition=Functions//Function definition
 python.colors.braces.and.operators.dot=Braces and Operators//Dot
index dc8bcfa307d010e5d7499bb112a709f4a9aa37b0..16c3ca8926bea68e70636da9aa0fcef20e6e446e 100644 (file)
@@ -94,6 +94,8 @@ public class PyHighlighter extends SyntaxHighlighterBase {
 
   public static final TextAttributesKey PY_FUNC_DEFINITION = TextAttributesKey.createTextAttributesKey("PY.FUNC_DEFINITION", FUNCTION_DECLARATION);
 
+  public static final TextAttributesKey PY_NESTED_FUNC_DEFINITION = TextAttributesKey.createTextAttributesKey("PY.NESTED_FUNC_DEFINITION", PY_FUNC_DEFINITION);
+
   public static final TextAttributesKey PY_PREDEFINED_DEFINITION = TextAttributesKey.createTextAttributesKey("PY.PREDEFINED_DEFINITION", PREDEFINED_SYMBOL);
 
   public static final TextAttributesKey PY_PREDEFINED_USAGE = TextAttributesKey.createTextAttributesKey("PY.PREDEFINED_USAGE", PREDEFINED_SYMBOL);
index aa77eff0436c9b8b1861beb3b7fe6ff6047bcbf0..b5f6f939f6626504cb368ad689ea191370f11afe 100644 (file)
@@ -54,6 +54,7 @@ public class PythonColorsPage implements RainbowColorSettingsPage, InspectionCol
     new AttributesDescriptor(PyBundle.message("python.colors.braces.and.operators.dot"), PyHighlighter.PY_DOT),
     
     new AttributesDescriptor(PyBundle.message("python.colors.functions.function.definition"), PyHighlighter.PY_FUNC_DEFINITION),
+    new AttributesDescriptor(PyBundle.message("python.colors.functions.nested.function.definition"), PyHighlighter.PY_NESTED_FUNC_DEFINITION),
     new AttributesDescriptor(PyBundle.message("python.colors.functions.function.call"), PyHighlighter.PY_FUNCTION_CALL),
     new AttributesDescriptor(PyBundle.message("python.colors.functions.method.call"), PyHighlighter.PY_METHOD_CALL),
     
@@ -78,6 +79,7 @@ public class PythonColorsPage implements RainbowColorSettingsPage, InspectionCol
     .put("predefined", PyHighlighter.PY_PREDEFINED_DEFINITION)
     .put("predefinedUsage", PyHighlighter.PY_PREDEFINED_USAGE)
     .put("funcDef", PyHighlighter.PY_FUNC_DEFINITION)
+    .put("nestedFuncDef", PyHighlighter.PY_NESTED_FUNC_DEFINITION)
     .put("classDef", PyHighlighter.PY_CLASS_DEFINITION)
     .put("builtin", PyHighlighter.PY_BUILTIN_NAME)
     .put("self", PyHighlighter.PY_SELF_PARAMETER)
@@ -132,17 +134,20 @@ public class PythonColorsPage implements RainbowColorSettingsPage, InspectionCol
       "" +
       RainbowHighlighter.generatePaletteExample("\n        ") + "\n" +
       "    \"\"\"</docComment>\n" +
+      "    def <nestedFuncDef>nested_func</nestedFuncDef>(<param>y</param>):\n" +
+      "        <call>print</call>(<param>y</param> + 1)\n" +
       "    <localVar>s</localVar> = (\"Test\", 2+3, {'a': 'b'}, f'{<param>x</param>!s:{\"^10\"}}')   # Comment\n" +
       "    <call>f</call>(<localVar>s</localVar>[0].<mcall>lower()</mcall>)\n" +
+      "    <call>nested_func(42)</call>" +
       "\n" +
       "class <classDef>Foo</classDef>:\n" +
       "    tags: <annotation>List[<builtin>str</builtin>]</annotation>\n" +
       "    def <predefined>__init__</predefined>(<self>self</self>: <annotation>Foo</annotation>):\n" +
       "        <localVar>byte_string</localVar>: <annotation><builtin>bytes</builtin></annotation> = b'newline:\\n also newline:\\x0a'\n" +
       "        <localVar>text_string</localVar> = u\"Cyrillic Я is \\u042f. Oops: \\u042g\"\n" +
-      "        <self>self</self>.<mcall>makeSense</mcall>(<kwarg>whatever</kwarg>=1)\n" +
+      "        <self>self</self>.<mcall>make_sense</mcall>(<kwarg>whatever</kwarg>=1)\n" +
       "    \n" +
-      "    def <funcDef>makeSense</funcDef>(<self>self</self>, <param>whatever</param>):\n" +
+      "    def <funcDef>make_sense</funcDef>(<self>self</self>, <param>whatever</param>):\n" +
       "        <self>self</self>.sense = <param>whatever</param>\n" +
       "\n" +
       "<localVar>x</localVar> = <builtin>len</builtin>('abc')\n" +
index 5d6c1102e0672437fdaa605ec1c0c1a1958a800f..1d9e7007b02f6a8456db8f575fdf90c1aadec1a3 100644 (file)
@@ -18,7 +18,10 @@ package com.jetbrains.python.validation;
 import com.intellij.lang.ASTNode;
 import com.intellij.openapi.project.IndexNotReadyException;
 import com.intellij.psi.PsiElement;
+import com.intellij.psi.util.PsiTreeUtil;
 import com.jetbrains.python.PyNames;
+import com.jetbrains.python.codeInsight.controlflow.ScopeOwner;
+import com.jetbrains.python.codeInsight.dataflow.scope.ScopeUtil;
 import com.jetbrains.python.highlighting.PyHighlighter;
 import com.jetbrains.python.psi.*;
 import org.jetbrains.annotations.NotNull;
@@ -41,8 +44,8 @@ public class PyDefinitionsAnnotator extends PyAnnotator {
 
   @Override
   public void visitPyFunction(PyFunction node) {
-    ASTNode name_node = node.getNameNode();
-    if (name_node != null) {
+    ASTNode nameNode = node.getNameNode();
+    if (nameNode != null) {
       final String name = node.getName();
       LanguageLevel languageLevel = LanguageLevel.forElement(node);
       if (PyNames.UNDERSCORED_ATTRIBUTES.contains(name) || PyNames.getBuiltinMethods(languageLevel).containsKey(name)) {
@@ -55,15 +58,20 @@ public class PyDefinitionsAnnotator extends PyAnnotator {
           catch (IndexNotReadyException ignored) {
           }
           if (new_style_class) {
-            addHighlightingAnnotation(name_node, PyHighlighter.PY_PREDEFINED_DEFINITION);
+            addHighlightingAnnotation(nameNode, PyHighlighter.PY_PREDEFINED_DEFINITION);
           }
         }
         else {
-          addHighlightingAnnotation(name_node, PyHighlighter.PY_PREDEFINED_DEFINITION);
+          addHighlightingAnnotation(nameNode, PyHighlighter.PY_PREDEFINED_DEFINITION);
         }
       }
       else {
-        addHighlightingAnnotation(name_node, PyHighlighter.PY_FUNC_DEFINITION);
+        if (ScopeUtil.getScopeOwner(node) instanceof PyFunction) {
+          addHighlightingAnnotation(nameNode, PyHighlighter.PY_NESTED_FUNC_DEFINITION);
+        }
+        else {
+          addHighlightingAnnotation(nameNode, PyHighlighter.PY_FUNC_DEFINITION);
+        }
       }
     }
   }
index de0afbc51a72172b722a1323c363975c05115317..d1fda3712e6e0cc716f7f5c1301c56d001c15916 100644 (file)
@@ -12,7 +12,7 @@ async = 1
 
 <error descr="Indent expected">d</error>ef <info descr="PY.FUNC_DEFINITION">regular</info>(<info descr="PY.PARAMETER">xs</info>):
 
-    <info descr="null">async</info> def <info descr="PY.FUNC_DEFINITION">quux</info>():
+    <info descr="null">async</info> def <info descr="PY.NESTED_FUNC_DEFINITION">quux</info>():
         <info descr="null">async</info> for x in xs:
             pass
 
diff --git a/python/testData/highlighting/nestedFunction.py b/python/testData/highlighting/nestedFunction.py
new file mode 100644 (file)
index 0000000..c824e25
--- /dev/null
@@ -0,0 +1,12 @@
+# func declarations are red
+def <info descr="PY.FUNC_DEFINITION" foreground="0xff0000" background="0x000000" effectcolor="0xffffff" effecttype="BOXED" fonttype="1">foo</info>():
+    def <info descr="PY.NESTED_FUNC_DEFINITION" foreground="0x00ff00" background="0x0000ff" effectcolor="0xffffff" effecttype="BOXED" fonttype="1">nested</info>():
+        return 42
+    return <info descr="PY.BUILTIN_NAME">False</info>
+
+
+def <info descr="PY.FUNC_DEFINITION" foreground="0xff0000" background="0x000000" effectcolor="0xffffff" effecttype="BOXED" fonttype="1">bar</info>():
+    class <info descr="PY.CLASS_DEFINITION" type="INFORMATION" foreground="0x0000ff" background="0x000000" effectcolor="0xffffff" effecttype="BOXED" fonttype="1">Clzz</info>:
+        def <info descr="PY.FUNC_DEFINITION" foreground="0xff0000" background="0x000000" effectcolor="0xffffff" effecttype="BOXED" fonttype="1">baz</info>():
+            return 42
+    return <info descr="PY.BUILTIN_NAME">False</info>
index 8dc9e510835fdf73dc2ab7ed2ca46704cf9eea24..17cd98e25db9ced2b8042b33a34e0e8fcd103c99 100644 (file)
@@ -1,5 +1,5 @@
 # func declarations are red
 def <info descr="PY.FUNC_DEFINITION" foreground="0xff0000" background="0x000000" effectcolor="0xffffff" effecttype="BOXED" fonttype="1">foo</info>():
-    def <info descr="PY.FUNC_DEFINITION" foreground="0xff0000" background="0x000000" effectcolor="0xffffff" effecttype="BOXED" fonttype="1">a</info>():
+    def <info descr="PY.NESTED_FUNC_DEFINITION" foreground="0xff0000" background="0x000000" effectcolor="0xffffff" effecttype="BOXED" fonttype="1">a</info>():
         yield 1
     return <info descr="PY.BUILTIN_NAME">False</info>
index ab22b0bc9a0d6651f5c2377fd146050e456a2761..da37f550ae4760da7e8c4528b5c45873da44ccc0 100644 (file)
@@ -206,6 +206,25 @@ public class PythonHighlightingTest extends PyTestCase {
     doTest();
   }
 
+  // PY-33235
+  public void testNestedFunction() {
+    EditorColorsScheme scheme = createTemporaryColorScheme();
+
+    TextAttributesKey xKey = TextAttributesKey.find("PY.CLASS_DEFINITION");
+    TextAttributes xAttributes = new TextAttributes(Color.blue, Color.black, Color.white, EffectType.BOXED, Font.BOLD);
+    scheme.setAttributes(xKey, xAttributes);
+
+    xKey = TextAttributesKey.find("PY.FUNC_DEFINITION");
+    xAttributes = new TextAttributes(Color.red, Color.black, Color.white, EffectType.BOXED, Font.BOLD);
+    scheme.setAttributes(xKey, xAttributes);
+
+    xKey = TextAttributesKey.find("PY.NESTED_FUNC_DEFINITION");
+    xAttributes = new TextAttributes(Color.green, Color.blue, Color.white, EffectType.BOXED, Font.BOLD);
+    scheme.setAttributes(xKey, xAttributes);
+
+    doTest();
+  }
+
   public void testAsync() {
     doTest(LanguageLevel.PYTHON35, true, true);
   }