From 11c12e56c9f7edc1d7591a07315ecdb6d7d09e87 Mon Sep 17 00:00:00 2001 From: "andrey.matveev" Date: Fri, 19 Jun 2020 19:09:32 +0700 Subject: [PATCH] PY-33235 Add highlighting annotator for nested functions GitOrigin-RevId: 0329f4af71bb86128531ab92c581a407339e1c7d --- .../messages/PyBundle.properties | 1 + .../python/highlighting/PyHighlighter.java | 2 ++ .../python/highlighting/PythonColorsPage.java | 9 +++++++-- .../validation/PyDefinitionsAnnotator.java | 18 +++++++++++++----- python/testData/highlighting/async.py | 2 +- .../testData/highlighting/nestedFunction.py | 12 ++++++++++++ .../highlighting/yieldInNestedFunction.py | 2 +- .../python/PythonHighlightingTest.java | 19 +++++++++++++++++++ 8 files changed, 56 insertions(+), 9 deletions(-) create mode 100644 python/testData/highlighting/nestedFunction.py 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); }