diff --git a/python/pluginResources/messages/PyBundle.properties b/python/pluginResources/messages/PyBundle.properties
index 5fa88502f911..17da9e244235 100644
--- a/python/pluginResources/messages/PyBundle.properties
+++ b/python/pluginResources/messages/PyBundle.properties
@@ -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
diff --git a/python/src/com/jetbrains/python/highlighting/PyHighlighter.java b/python/src/com/jetbrains/python/highlighting/PyHighlighter.java
index dc8bcfa307d0..16c3ca8926be 100644
--- a/python/src/com/jetbrains/python/highlighting/PyHighlighter.java
+++ b/python/src/com/jetbrains/python/highlighting/PyHighlighter.java
@@ -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);
diff --git a/python/src/com/jetbrains/python/highlighting/PythonColorsPage.java b/python/src/com/jetbrains/python/highlighting/PythonColorsPage.java
index aa77eff0436c..b5f6f939f662 100644
--- a/python/src/com/jetbrains/python/highlighting/PythonColorsPage.java
+++ b/python/src/com/jetbrains/python/highlighting/PythonColorsPage.java
@@ -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" +
" \"\"\"\n" +
+ " def nested_func(y):\n" +
+ " print(y + 1)\n" +
" s = (\"Test\", 2+3, {'a': 'b'}, f'{x!s:{\"^10\"}}') # Comment\n" +
" f(s[0].lower())\n" +
+ " nested_func(42)" +
"\n" +
"class Foo:\n" +
" tags: List[str]\n" +
" def __init__(self: Foo):\n" +
" byte_string: bytes = b'newline:\\n also newline:\\x0a'\n" +
" text_string = u\"Cyrillic Я is \\u042f. Oops: \\u042g\"\n" +
- " self.makeSense(whatever=1)\n" +
+ " self.make_sense(whatever=1)\n" +
" \n" +
- " def makeSense(self, whatever):\n" +
+ " def make_sense(self, whatever):\n" +
" self.sense = whatever\n" +
"\n" +
"x = len('abc')\n" +
diff --git a/python/src/com/jetbrains/python/validation/PyDefinitionsAnnotator.java b/python/src/com/jetbrains/python/validation/PyDefinitionsAnnotator.java
index 5d6c1102e067..1d9e7007b02f 100644
--- a/python/src/com/jetbrains/python/validation/PyDefinitionsAnnotator.java
+++ b/python/src/com/jetbrains/python/validation/PyDefinitionsAnnotator.java
@@ -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);
+ }
}
}
}
diff --git a/python/testData/highlighting/async.py b/python/testData/highlighting/async.py
index de0afbc51a72..d1fda3712e6e 100644
--- a/python/testData/highlighting/async.py
+++ b/python/testData/highlighting/async.py
@@ -12,7 +12,7 @@ async = 1
def regular(xs):
- async def quux():
+ async def quux():
async for x in xs:
pass
diff --git a/python/testData/highlighting/nestedFunction.py b/python/testData/highlighting/nestedFunction.py
new file mode 100644
index 000000000000..c824e2537819
--- /dev/null
+++ b/python/testData/highlighting/nestedFunction.py
@@ -0,0 +1,12 @@
+# func declarations are red
+def foo():
+ def nested():
+ return 42
+ return False
+
+
+def bar():
+ class Clzz:
+ def baz():
+ return 42
+ return False
diff --git a/python/testData/highlighting/yieldInNestedFunction.py b/python/testData/highlighting/yieldInNestedFunction.py
index 8dc9e510835f..17cd98e25db9 100644
--- a/python/testData/highlighting/yieldInNestedFunction.py
+++ b/python/testData/highlighting/yieldInNestedFunction.py
@@ -1,5 +1,5 @@
# func declarations are red
def foo():
- def a():
+ def a():
yield 1
return False
diff --git a/python/testSrc/com/jetbrains/python/PythonHighlightingTest.java b/python/testSrc/com/jetbrains/python/PythonHighlightingTest.java
index ab22b0bc9a0d..da37f550ae47 100644
--- a/python/testSrc/com/jetbrains/python/PythonHighlightingTest.java
+++ b/python/testSrc/com/jetbrains/python/PythonHighlightingTest.java
@@ -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);
}