PY-33235 Add highlighting annotator for nested functions
[idea/community.git] / python / src / com / jetbrains / python / highlighting / PythonColorsPage.java
1 // Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
2 package com.jetbrains.python.highlighting;
3
4 import com.google.common.collect.ImmutableMap;
5 import com.intellij.application.options.colors.InspectionColorSettingsPage;
6 import com.intellij.codeHighlighting.RainbowHighlighter;
7 import com.intellij.lang.Language;
8 import com.intellij.openapi.editor.DefaultLanguageHighlighterColors;
9 import com.intellij.openapi.editor.colors.TextAttributesKey;
10 import com.intellij.openapi.fileTypes.SyntaxHighlighter;
11 import com.intellij.openapi.fileTypes.SyntaxHighlighterFactory;
12 import com.intellij.openapi.options.colors.AttributesDescriptor;
13 import com.intellij.openapi.options.colors.ColorDescriptor;
14 import com.intellij.openapi.options.colors.RainbowColorSettingsPage;
15 import com.intellij.psi.codeStyle.DisplayPriority;
16 import com.intellij.psi.codeStyle.DisplayPrioritySortable;
17 import com.intellij.util.PlatformUtils;
18 import com.jetbrains.python.PyBundle;
19 import com.jetbrains.python.PythonFileType;
20 import com.jetbrains.python.PythonLanguage;
21 import com.jetbrains.python.psi.LanguageLevel;
22 import org.jetbrains.annotations.NonNls;
23 import org.jetbrains.annotations.NotNull;
24 import org.jetbrains.annotations.Nullable;
25
26 import javax.swing.*;
27 import java.util.Map;
28
29 /**
30  * @author yole
31  */
32 public class PythonColorsPage implements RainbowColorSettingsPage, InspectionColorSettingsPage, DisplayPrioritySortable {
33   private static final AttributesDescriptor[] ATTRS = new AttributesDescriptor[] {
34     new AttributesDescriptor(PyBundle.message("python.colors.number"), PyHighlighter.PY_NUMBER),
35     new AttributesDescriptor(PyBundle.message("python.colors.keyword"), PyHighlighter.PY_KEYWORD),
36     new AttributesDescriptor(PyBundle.message("python.colors.line.comment"), PyHighlighter.PY_LINE_COMMENT),
37
38     new AttributesDescriptor(PyBundle.message("python.colors.string.binary.bytes"), PyHighlighter.PY_BYTE_STRING),
39     new AttributesDescriptor(PyBundle.message("python.colors.string.text.unicode"), PyHighlighter.PY_UNICODE_STRING),
40     new AttributesDescriptor(PyBundle.message("python.colors.string.escape.sequence.valid"), PyHighlighter.PY_VALID_STRING_ESCAPE),
41     new AttributesDescriptor(PyBundle.message("python.colors.string.escape.sequence.invalid"), PyHighlighter.PY_INVALID_STRING_ESCAPE),
42     new AttributesDescriptor(PyBundle.message("python.colors.string.f.string.expression.braces"), PyHighlighter.PY_FSTRING_FRAGMENT_BRACES),
43     new AttributesDescriptor(PyBundle.message("python.colors.string.f.string.type.conversion"), PyHighlighter.PY_FSTRING_FRAGMENT_TYPE_CONVERSION),
44     new AttributesDescriptor(PyBundle.message("python.colors.string.f.string.format.specifier.start"), PyHighlighter.PY_FSTRING_FRAGMENT_COLON),
45
46     new AttributesDescriptor(PyBundle.message("python.colors.docstring.text"), PyHighlighter.PY_DOC_COMMENT),
47     new AttributesDescriptor(PyBundle.message("python.colors.docstring.tag"), PyHighlighter.PY_DOC_COMMENT_TAG),
48     
49     new AttributesDescriptor(PyBundle.message("python.colors.braces.and.operators.operation.sign"), PyHighlighter.PY_OPERATION_SIGN),
50     new AttributesDescriptor(PyBundle.message("python.colors.braces.and.operators.parentheses"), PyHighlighter.PY_PARENTHS),
51     new AttributesDescriptor(PyBundle.message("python.colors.braces.and.operators.brackets"), PyHighlighter.PY_BRACKETS),
52     new AttributesDescriptor(PyBundle.message("python.colors.braces.and.operators.braces"), PyHighlighter.PY_BRACES),
53     new AttributesDescriptor(PyBundle.message("python.colors.braces.and.operators.comma"), PyHighlighter.PY_COMMA),
54     new AttributesDescriptor(PyBundle.message("python.colors.braces.and.operators.dot"), PyHighlighter.PY_DOT),
55     
56     new AttributesDescriptor(PyBundle.message("python.colors.functions.function.definition"), PyHighlighter.PY_FUNC_DEFINITION),
57     new AttributesDescriptor(PyBundle.message("python.colors.functions.nested.function.definition"), PyHighlighter.PY_NESTED_FUNC_DEFINITION),
58     new AttributesDescriptor(PyBundle.message("python.colors.functions.function.call"), PyHighlighter.PY_FUNCTION_CALL),
59     new AttributesDescriptor(PyBundle.message("python.colors.functions.method.call"), PyHighlighter.PY_METHOD_CALL),
60     
61     new AttributesDescriptor(PyBundle.message("python.colors.parameters.parameter"), PyHighlighter.PY_PARAMETER),
62     new AttributesDescriptor(PyBundle.message("python.colors.parameters.self.parameter"), PyHighlighter.PY_SELF_PARAMETER),
63     
64     new AttributesDescriptor(PyBundle.message("python.colors.keyword.argument"), PyHighlighter.PY_KEYWORD_ARGUMENT),
65
66     new AttributesDescriptor(PyBundle.message("python.colors.special.names.definition"), PyHighlighter.PY_PREDEFINED_DEFINITION),
67     new AttributesDescriptor(PyBundle.message("python.colors.special.names.usage"), PyHighlighter.PY_PREDEFINED_USAGE),
68     
69     new AttributesDescriptor(PyBundle.message("python.colors.built.in.name"), PyHighlighter.PY_BUILTIN_NAME),
70     new AttributesDescriptor(PyBundle.message("python.colors.decorator"), PyHighlighter.PY_DECORATOR),
71     new AttributesDescriptor(PyBundle.message("python.colors.class.definition"), PyHighlighter.PY_CLASS_DEFINITION),
72     new AttributesDescriptor(PyBundle.message("python.colors.type.annotation"), PyHighlighter.PY_ANNOTATION),
73   };
74
75   @NonNls private static final Map<String,TextAttributesKey> ourTagToDescriptorMap = ImmutableMap.<String, TextAttributesKey>builder()
76     .put("docComment", PyHighlighter.PY_DOC_COMMENT)
77     .put("docCommentTag", PyHighlighter.PY_DOC_COMMENT_TAG)
78     .put("decorator", PyHighlighter.PY_DECORATOR)
79     .put("predefined", PyHighlighter.PY_PREDEFINED_DEFINITION)
80     .put("predefinedUsage", PyHighlighter.PY_PREDEFINED_USAGE)
81     .put("funcDef", PyHighlighter.PY_FUNC_DEFINITION)
82     .put("nestedFuncDef", PyHighlighter.PY_NESTED_FUNC_DEFINITION)
83     .put("classDef", PyHighlighter.PY_CLASS_DEFINITION)
84     .put("builtin", PyHighlighter.PY_BUILTIN_NAME)
85     .put("self", PyHighlighter.PY_SELF_PARAMETER)
86     .put("param", PyHighlighter.PY_PARAMETER)
87     .put("kwarg", PyHighlighter.PY_KEYWORD_ARGUMENT)
88     .put("call", PyHighlighter.PY_FUNCTION_CALL)
89     .put("mcall", PyHighlighter.PY_METHOD_CALL)
90     .put("annotation", PyHighlighter.PY_ANNOTATION)
91     .put("localVar", DefaultLanguageHighlighterColors.LOCAL_VARIABLE)
92     .putAll(RainbowHighlighter.createRainbowHLM())
93     .build();
94
95   @Override
96   @NotNull
97   public String getDisplayName() {
98     return PyBundle.message("python.colors.python");
99   }
100
101   @Override
102   public Icon getIcon() {
103     return PythonFileType.INSTANCE.getIcon();
104   }
105
106   @Override
107   public AttributesDescriptor @NotNull [] getAttributeDescriptors() {
108     return ATTRS;
109   }
110
111   @Override
112   public ColorDescriptor @NotNull [] getColorDescriptors() {
113     return ColorDescriptor.EMPTY_ARRAY;
114   }
115
116   @Override
117   @NotNull
118   public SyntaxHighlighter getHighlighter() {
119     final SyntaxHighlighterFactory factory = SyntaxHighlighterFactory.LANGUAGE_FACTORY.forLanguage(PythonLanguage.getInstance());
120     if (factory instanceof PySyntaxHighlighterFactory) {
121       return ((PySyntaxHighlighterFactory)factory).getSyntaxHighlighterForLanguageLevel(LanguageLevel.getLatest());
122     }
123     return factory.getSyntaxHighlighter(null, null);
124   }
125
126   @Override
127   @NotNull
128   public String getDemoText() {
129     return
130       "@<decorator>decorator</decorator>(<kwarg>param</kwarg>=1)\n" +
131       "def f(<param>x</param>):\n" +
132       "    <docComment>\"\"\" Syntax Highlighting Demo\n" +
133       "        <docCommentTag>@param</docCommentTag> x Parameter\n" +
134       "" +
135       RainbowHighlighter.generatePaletteExample("\n        ") + "\n" +
136       "    \"\"\"</docComment>\n" +
137       "    def <nestedFuncDef>nested_func</nestedFuncDef>(<param>y</param>):\n" +
138       "        <call>print</call>(<param>y</param> + 1)\n" +
139       "    <localVar>s</localVar> = (\"Test\", 2+3, {'a': 'b'}, f'{<param>x</param>!s:{\"^10\"}}')   # Comment\n" +
140       "    <call>f</call>(<localVar>s</localVar>[0].<mcall>lower()</mcall>)\n" +
141       "    <call>nested_func(42)</call>" +
142       "\n" +
143       "class <classDef>Foo</classDef>:\n" +
144       "    tags: <annotation>List[<builtin>str</builtin>]</annotation>\n" +
145       "    def <predefined>__init__</predefined>(<self>self</self>: <annotation>Foo</annotation>):\n" +
146       "        <localVar>byte_string</localVar>: <annotation><builtin>bytes</builtin></annotation> = b'newline:\\n also newline:\\x0a'\n" +
147       "        <localVar>text_string</localVar> = u\"Cyrillic Я is \\u042f. Oops: \\u042g\"\n" +
148       "        <self>self</self>.<mcall>make_sense</mcall>(<kwarg>whatever</kwarg>=1)\n" +
149       "    \n" +
150       "    def <funcDef>make_sense</funcDef>(<self>self</self>, <param>whatever</param>):\n" +
151       "        <self>self</self>.sense = <param>whatever</param>\n" +
152       "\n" +
153       "<localVar>x</localVar> = <builtin>len</builtin>('abc')\n" +
154       "print(f.<predefinedUsage>__doc__</predefinedUsage>)"
155     ;
156   }
157
158   @Override
159   public Map<String, TextAttributesKey> getAdditionalHighlightingTagToDescriptorMap() {
160     return ourTagToDescriptorMap;
161   }
162
163   @Override
164   public DisplayPriority getPriority() {
165     return PlatformUtils.isPyCharm() ? DisplayPriority.KEY_LANGUAGE_SETTINGS : DisplayPriority.LANGUAGE_SETTINGS;
166   }
167
168   @Override
169   public boolean isRainbowType(TextAttributesKey type) {
170     return PyRainbowVisitor.Holder.getHIGHLIGHTING_KEYS().contains(type);
171   }
172
173   @Nullable
174   @Override
175   public Language getLanguage() {
176     return PythonLanguage.INSTANCE;
177   }
178 }