Adds highligting of class and function definitions, decorators, and builtins.

Fixes PY-2.
       No test for builtin highlighting yet.
This commit is contained in:
Dmitry Cheryasov
2009-01-15 14:48:48 +03:00
parent d494181069
commit ac1b2de892
11 changed files with 264 additions and 52 deletions

View File

@@ -71,6 +71,7 @@ ANN.cant.assign.to.lambda=can't assign to lambda
ANN.break.outside.loop='break' outside loop
ANN.continue.outside.loop='continue' outside loop
ANN.cant.continue.in.finally='continue' not supported inside 'finally' clause
ANN.default.except.must.be.last=default 'except:' must be last
### parsing
PARSE.expected.expression=expression expected

View File

@@ -18,13 +18,15 @@ package com.jetbrains.python;
import com.intellij.lexer.Lexer;
import com.intellij.openapi.editor.HighlighterColors;
import com.intellij.openapi.editor.SyntaxHighlighterColors;
import static com.intellij.openapi.editor.SyntaxHighlighterColors.*;
import com.intellij.openapi.editor.colors.TextAttributesKey;
import com.intellij.openapi.editor.markup.TextAttributes;
import com.intellij.openapi.fileTypes.SyntaxHighlighterBase;
import com.intellij.psi.tree.IElementType;
import com.jetbrains.python.lexer.PythonFutureAwareLexer;
import org.jetbrains.annotations.NotNull;
import java.awt.*;
import java.util.HashMap;
import java.util.Map;
@@ -43,37 +45,54 @@ public class PyHighlighter extends SyntaxHighlighterBase {
return new PythonFutureAwareLexer();
}
static final TextAttributesKey PY_KEYWORD =
TextAttributesKey.createTextAttributesKey("PY.KEYWORD", SyntaxHighlighterColors.KEYWORD.getDefaultAttributes());
private static TextAttributesKey _copy(String name, TextAttributesKey src) {
return TextAttributesKey.createTextAttributesKey(name, src.getDefaultAttributes().clone());
}
static final TextAttributesKey PY_STRING =
TextAttributesKey.createTextAttributesKey("PY.STRING", SyntaxHighlighterColors.STRING.getDefaultAttributes());
static final TextAttributesKey PY_NUMBER =
TextAttributesKey.createTextAttributesKey("PY.NUMBER", SyntaxHighlighterColors.NUMBER.getDefaultAttributes());
static final TextAttributesKey PY_KEYWORD = _copy("PY.KEYWORD", KEYWORD);
static final TextAttributesKey PY_LINE_COMMENT =
TextAttributesKey.createTextAttributesKey("PY.LINE_COMMENT", SyntaxHighlighterColors.LINE_COMMENT.getDefaultAttributes());
static final TextAttributesKey PY_STRING = _copy("PY.STRING", STRING);
static final TextAttributesKey PY_NUMBER = _copy("PY.NUMBER", NUMBER);
static final TextAttributesKey PY_OPERATION_SIGN =
TextAttributesKey.createTextAttributesKey("PY.OPERATION_SIGN", SyntaxHighlighterColors.OPERATION_SIGN.getDefaultAttributes());
static final TextAttributesKey PY_LINE_COMMENT = _copy("PY.LINE_COMMENT", LINE_COMMENT);
static final TextAttributesKey PY_PARENTHS =
TextAttributesKey.createTextAttributesKey("PY.PARENTHS", SyntaxHighlighterColors.PARENTHS.getDefaultAttributes());
static final TextAttributesKey PY_OPERATION_SIGN = _copy("PY.OPERATION_SIGN", OPERATION_SIGN);
static final TextAttributesKey PY_BRACKETS =
TextAttributesKey.createTextAttributesKey("PY.BRACKETS", SyntaxHighlighterColors.BRACKETS.getDefaultAttributes());
static final TextAttributesKey PY_PARENTHS = _copy("PY.PARENTHS", PARENTHS);
static final TextAttributesKey PY_BRACES =
TextAttributesKey.createTextAttributesKey("PY.BRACES", SyntaxHighlighterColors.BRACES.getDefaultAttributes());
static final TextAttributesKey PY_BRACKETS = _copy("PY.BRACKETS", BRACKETS);
static final TextAttributesKey PY_COMMA =
TextAttributesKey.createTextAttributesKey("PY.COMMA", SyntaxHighlighterColors.COMMA.getDefaultAttributes());
static final TextAttributesKey PY_BRACES = _copy("PY.BRACES", BRACES);
static final TextAttributesKey PY_DOT =
TextAttributesKey.createTextAttributesKey("PY.DOT", SyntaxHighlighterColors.DOT.getDefaultAttributes());
static final TextAttributesKey PY_COMMA = _copy("PY.COMMA", COMMA);
public static final TextAttributesKey PY_DOC_COMMENT =
TextAttributesKey.createTextAttributesKey("PY.DOC_COMMENT", SyntaxHighlighterColors.DOC_COMMENT.getDefaultAttributes());
static final TextAttributesKey PY_DOT = _copy("PY.DOT", DOT);
public static final TextAttributesKey PY_DOC_COMMENT = _copy("PY.DOC_COMMENT", DOC_COMMENT);
public static final TextAttributesKey PY_DECORATOR = TextAttributesKey.createTextAttributesKey(
"PY.DECORATOR", new TextAttributes(Color.blue.darker(), null, null, null, Font.PLAIN)
);
public static final TextAttributesKey PY_CLASS_DEFINITION = TextAttributesKey.createTextAttributesKey(
"PY.CLASS_DEFINITION", new TextAttributes(Color.black, null, null, null, Font.BOLD)
);
public static final TextAttributesKey PY_FUNC_DEFINITION = TextAttributesKey.createTextAttributesKey(
"PY.FUNC_DEFINITION", new TextAttributes(Color.black, null, null, null, Font.BOLD)
);
public static final TextAttributesKey PY_PREDEFINED_DEFINITION = TextAttributesKey.createTextAttributesKey(
"PY.PREDEFINED_DEFINITION", new TextAttributes(Color.magenta.darker(), null, null, null, Font.BOLD)
);
public static final TextAttributesKey PY_PREDEFINED_USAGE = TextAttributesKey.createTextAttributesKey(
"PY.PREDEFINED_USAGE", new TextAttributes(Color.magenta.darker(), null, null, null, Font.PLAIN)
);
public static final TextAttributesKey PY_BUILTIN_NAME = TextAttributesKey.createTextAttributesKey(
"PY.BUILTIN_NAME", new TextAttributes(KEYWORD.getDefaultAttributes().getForegroundColor(), null, null, null, Font.PLAIN)
);
public PyHighlighter() {
keys1 = new HashMap<IElementType, TextAttributesKey>();

View File

@@ -17,23 +17,35 @@ import java.util.Map;
*/
public class PythonColorsPage implements ColorSettingsPage {
private static final AttributesDescriptor[] ATTRS = new AttributesDescriptor[] {
new AttributesDescriptor("Keyword", PyHighlighter.PY_KEYWORD),
new AttributesDescriptor("String", PyHighlighter.PY_STRING),
new AttributesDescriptor("Number", PyHighlighter.PY_NUMBER),
new AttributesDescriptor("Line Comment", PyHighlighter.PY_LINE_COMMENT),
new AttributesDescriptor("Operation Sign", PyHighlighter.PY_OPERATION_SIGN),
new AttributesDescriptor("Parentheses", PyHighlighter.PY_PARENTHS),
new AttributesDescriptor("Brackets", PyHighlighter.PY_BRACKETS),
new AttributesDescriptor("Braces", PyHighlighter.PY_BRACES),
new AttributesDescriptor("Comma", PyHighlighter.PY_COMMA),
new AttributesDescriptor("Dot", PyHighlighter.PY_DOT),
new AttributesDescriptor("Doc Comment", PyHighlighter.PY_DOC_COMMENT)
new AttributesDescriptor("Keyword", PyHighlighter.PY_KEYWORD),
new AttributesDescriptor("String", PyHighlighter.PY_STRING),
new AttributesDescriptor("Number", PyHighlighter.PY_NUMBER),
new AttributesDescriptor("Line Comment", PyHighlighter.PY_LINE_COMMENT),
new AttributesDescriptor("Operation Sign", PyHighlighter.PY_OPERATION_SIGN),
new AttributesDescriptor("Parentheses", PyHighlighter.PY_PARENTHS),
new AttributesDescriptor("Brackets", PyHighlighter.PY_BRACKETS),
new AttributesDescriptor("Braces", PyHighlighter.PY_BRACES),
new AttributesDescriptor("Comma", PyHighlighter.PY_COMMA),
new AttributesDescriptor("Dot", PyHighlighter.PY_DOT),
new AttributesDescriptor("Function definition", PyHighlighter.PY_FUNC_DEFINITION),
new AttributesDescriptor("Class definition", PyHighlighter.PY_CLASS_DEFINITION),
new AttributesDescriptor("Doc Comment", PyHighlighter.PY_DOC_COMMENT),
new AttributesDescriptor("Predefined item definition", PyHighlighter.PY_PREDEFINED_DEFINITION),
new AttributesDescriptor("Decorator", PyHighlighter.PY_DECORATOR),
new AttributesDescriptor("Built-in name", PyHighlighter.PY_BUILTIN_NAME),
new AttributesDescriptor("Predefined name", PyHighlighter.PY_PREDEFINED_USAGE)
};
@NonNls private static final HashMap<String,TextAttributesKey> ourTagToDescriptorMap = new HashMap<String, TextAttributesKey>();
static {
ourTagToDescriptorMap.put("docComment", PyHighlighter.PY_DOC_COMMENT);
ourTagToDescriptorMap.put("decorator", PyHighlighter.PY_DECORATOR);
ourTagToDescriptorMap.put("predefined", PyHighlighter.PY_PREDEFINED_DEFINITION);
ourTagToDescriptorMap.put("predefinedUsage", PyHighlighter.PY_PREDEFINED_USAGE);
ourTagToDescriptorMap.put("funcDef", PyHighlighter.PY_FUNC_DEFINITION);
ourTagToDescriptorMap.put("classDef", PyHighlighter.PY_CLASS_DEFINITION);
ourTagToDescriptorMap.put("builtin", PyHighlighter.PY_BUILTIN_NAME);
}
@NotNull
@@ -64,10 +76,23 @@ public class PythonColorsPage implements ColorSettingsPage {
@NotNull
public String getDemoText() {
return "def f():\n" +
" <docComment>\"\"\" Syntax Highlighting Demo \"\"\"</docComment>\n" +
" s = (\"Test\", 2+3, {'a': 'b'}) # Comment\n" +
" print s[0].lower()";
return
"@<decorator>decorator</decorator>(param=1)\n" +
"def f(x):\n" +
" <docComment>\"\"\" Syntax Highlighting Demo \"\"\"</docComment>\n" +
" s = (\"Test\", 2+3, {'a': 'b'}, x) # Comment\n" +
" print s[0].lower()\n"+
"\n"+
"class <classDef>Foo</classDef>:\n"+
" def <predefined>__init__</predefined>(self):\n" +
" self.sense = None\n" +
" \n" +
" def <funcDef>makeSense</funcDef>(self, whatever):\n"+
" self.sense = whatever\n"+
"\n"+
"x = <builtin>len</builtin>('abc')\n"+
"print(f.<predefinedUsage>__doc__</predefinedUsage>)"
;
}
public Map<String, TextAttributesKey> getAdditionalHighlightingTagToDescriptorMap() {

View File

@@ -35,6 +35,8 @@ public class PythonLanguage extends Language {
_annotators.add(DocStringAnnotator.class);
_annotators.add(ImportAnnotator.class);
_annotators.add(StringConstantAnnotator.class);
_annotators.add(PyDefinitionsAnnotator.class);
_annotators.add(PyBuiltinAnnotator.class);
}

View File

@@ -387,9 +387,11 @@ public class PythonSdkType extends SdkType {
new String[] {bin_path, gen3_file.getPath(), "-d", stubsRoot, modname}
);
if (gen_result.exitValue() != 0) {
StringBuffer sb = new StringBuffer();
for (String err_line : gen_result.getStderr()) {
LOG.error(err_line);
sb.append(err_line).append("\n");
}
LOG.error(sb.toString());
}
}
}

View File

@@ -0,0 +1,50 @@
package com.jetbrains.python.validation;
import com.intellij.lang.ASTNode;
import com.intellij.lang.annotation.Annotation;
import com.intellij.psi.ResolveResult;
import com.jetbrains.python.PyHighlighter;
import com.jetbrains.python.PyNames;
import com.jetbrains.python.PyTokenTypes;
import com.jetbrains.python.psi.PyClass;
import com.jetbrains.python.psi.PyReferenceExpression;
import com.jetbrains.python.psi.PyResolveUtil;
import com.jetbrains.python.psi.impl.PyBuiltinCache;
/**
* Marks built-in names.
* User: dcheryasov
* Date: Jan 10, 2009 12:17:15 PM
*/
public class PyBuiltinAnnotator extends PyAnnotator {
@Override
public void visitPyReferenceExpression(PyReferenceExpression node) {
if (PyNames.UnderscoredNames.contains(node.getName())) {
// things like __len__
if (
(node.getQualifier() != null) // foo.__len__
|| (PyResolveUtil.getConcealingParent(node) instanceof PyClass) // class Foo: ... __len__ = myLenImpl
) {
final ASTNode astNode = node.getNode();
if (astNode != null) {
ASTNode tgt = astNode.findChildByType(PyTokenTypes.IDENTIFIER); // only the id, not all qualifiers subtree
if (tgt != null) {
Annotation ann = getHolder().createInfoAnnotation(tgt, null);
ann.setTextAttributes(PyHighlighter.PY_PREDEFINED_USAGE);
}
}
}
}
else if (node.getQualifier() == null) {
// things like len()
for (ResolveResult resolved : node.multiResolve(false)) { // things like constructors give multiple results
if (PyBuiltinCache.hasInBuiltins(resolved.getElement())) {
Annotation ann = getHolder().createInfoAnnotation(node, null);
ann.setTextAttributes(PyHighlighter.PY_BUILTIN_NAME);
break;
}
}
}
}
}

View File

@@ -0,0 +1,64 @@
package com.jetbrains.python.validation;
import com.intellij.lang.ASTNode;
import com.intellij.lang.annotation.Annotation;
import com.intellij.psi.PsiElement;
import com.jetbrains.python.PyHighlighter;
import com.jetbrains.python.PyNames;
import com.jetbrains.python.PyTokenTypes;
import com.jetbrains.python.psi.PyClass;
import com.jetbrains.python.psi.PyDecorator;
import com.jetbrains.python.psi.PyDecoratorList;
import com.jetbrains.python.psi.PyFunction;
/**
* Highlights class definitions, functrion definitions, and decorators.
* User: dcheryasov
* Date: Jan 9, 2009 9:53:38 AM
*/
public class PyDefinitionsAnnotator extends PyAnnotator {
@Override
public void visitPyClass(PyClass class_node) {
final ASTNode astNode = class_node.getNode();
if (astNode != null) {
ASTNode name_node = astNode.findChildByType(PyTokenTypes.IDENTIFIER);
if (name_node != null) {
Annotation ann = getHolder().createInfoAnnotation(name_node, null);
ann.setTextAttributes(PyHighlighter.PY_CLASS_DEFINITION);
}
}
}
@Override
public void visitPyFunction(PyFunction node) {
ASTNode name_node = node.getNameNode();
if (name_node != null) {
Annotation ann = getHolder().createInfoAnnotation(name_node, null);
if (PyNames.UnderscoredNames.contains(node.getName())) {
ann.setTextAttributes(PyHighlighter.PY_PREDEFINED_DEFINITION);
}
else ann.setTextAttributes(PyHighlighter.PY_FUNC_DEFINITION);
}
}
@Override
public void visitPyDecoratorList(PyDecoratorList node) {
PyDecorator[] decos = node.getDecorators();
for (PyDecorator deco : decos) {
highlightDecorator(deco);
}
}
private void highlightDecorator(PyDecorator node) {
// highlight only the identifier
PsiElement mk = node.getFirstChild(); // the '@'
if (mk != null) {
mk = mk.getNextSibling(); // ref
if (mk != null) {
Annotation ann = getHolder().createInfoAnnotation(mk, null);
ann.setTextAttributes(PyHighlighter.PY_DECORATOR);
}
}
}
}

View File

@@ -16,15 +16,14 @@
package com.jetbrains.python.validation;
import com.jetbrains.python.psi.PyTryExceptStatement;
import com.jetbrains.python.PyBundle;
import com.jetbrains.python.psi.PyExceptBlock;
import com.jetbrains.python.psi.PyTryExceptStatement;
/**
* Created by IntelliJ IDEA.
* Marks misplaced default 'except' clauses.
* User: yole
* Date: 13.06.2005
* Time: 13:49:18
* To change this template use File | Settings | File Templates.
*/
public class TryExceptAnnotator extends PyAnnotator {
@Override public void visitPyTryExceptStatement(final PyTryExceptStatement node) {
@@ -32,7 +31,7 @@ public class TryExceptAnnotator extends PyAnnotator {
boolean haveDefaultExcept = false;
for(PyExceptBlock block: exceptBlocks) {
if (haveDefaultExcept) {
getHolder().createErrorAnnotation(block, "default 'except': must be last");
getHolder().createErrorAnnotation(block, PyBundle.message("ANN.default.except.must.be.last"));
}
if (block.getExceptClass() == null) {
haveDefaultExcept = true;

View File

@@ -0,0 +1,17 @@
# bg is always black.
# effect is white
# func decl: red bold
# class decl: blue bold
# predefined decl: green bold
def <info descr="null" type="INFORMATION" foreground="0xff0000" background="0x000000" effectcolor="0xffffff" effecttype="BOXED" fonttype="1">foo</info>():
pass
class <info descr="null" type="INFORMATION" foreground="0x0000ff" background="0x000000" effectcolor="0xffffff" effecttype="BOXED" fonttype="1">Moo</info>:
def <info descr="null" type="INFORMATION" foreground="0x00ff00" background="0x000000" effectcolor="0xffffff" effecttype="BOXED" fonttype="1">__init__</info>(self):
pass
def <info descr="null" type="INFORMATION" foreground="0xff0000" background="0x000000" effectcolor="0xffffff" effecttype="BOXED" fonttype="1">doodle</info>(self):
pass
def <info descr="null" type="INFORMATION" foreground="0xff0000" background="0x000000" effectcolor="0xffffff" effecttype="BOXED" fonttype="1">__made_up__</info>(self):
return None

View File

@@ -1,4 +1,5 @@
def foo():
def a():
# func declarations are red
def <info descr="null" type="INFORMATION" foreground="0xff0000" background="0x000000" effectcolor="0xffffff" effecttype="BOXED" fonttype="1">foo</info>():
def <info descr="null" type="INFORMATION" foreground="0xff0000" background="0x000000" effectcolor="0xffffff" effecttype="BOXED" fonttype="1">a</info>():
yield 1
return False

View File

@@ -2,6 +2,13 @@ package com.jetbrains.python;
import com.intellij.codeInsight.daemon.DaemonAnalyzerTestCase;
import com.intellij.openapi.application.PathManager;
import com.intellij.openapi.editor.colors.EditorColorsManager;
import com.intellij.openapi.editor.colors.EditorColorsScheme;
import com.intellij.openapi.editor.colors.TextAttributesKey;
import com.intellij.openapi.editor.markup.TextAttributes;
import com.intellij.openapi.editor.markup.EffectType;
import java.awt.*;
/**
* @author yole
@@ -11,6 +18,27 @@ public class PythonHighlightingTest extends DaemonAnalyzerTestCase {
return PathManager.getHomePath() + "/plugins/python/testData/highlighting/";
}
public void testDeclarations() throws Exception {
EditorColorsManager manager = EditorColorsManager.getInstance();
EditorColorsScheme scheme = (EditorColorsScheme)manager.getGlobalScheme().clone();
manager.addColorsScheme(scheme);
EditorColorsManager.getInstance().setGlobalScheme(scheme);
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.PREDEFINED_DEFINITION");
xAttributes = new TextAttributes(Color.green, Color.black, Color.white, EffectType.BOXED, Font.BOLD);
scheme.setAttributes(xKey, xAttributes);
doTest();
}
public void testReturnOutsideOfFunction() throws Exception {
doTest();
}
@@ -39,13 +67,17 @@ public class PythonHighlightingTest extends DaemonAnalyzerTestCase {
doTest();
}
/* TODO: move to inspection test
public void testMethodParams() throws Exception {
doTest();
}
*/
public void testYieldInNestedFunction() throws Exception {
// highlight func declaration first, lest we get an "Extra fragment highlighted" error.
EditorColorsManager manager = EditorColorsManager.getInstance();
EditorColorsScheme scheme = (EditorColorsScheme)manager.getGlobalScheme().clone();
manager.addColorsScheme(scheme);
EditorColorsManager.getInstance().setGlobalScheme(scheme);
TextAttributesKey xKey = TextAttributesKey.find("PY.FUNC_DEFINITION");
TextAttributes xAttributes = new TextAttributes(Color.red, Color.black, Color.white, EffectType.BOXED, Font.BOLD);
scheme.setAttributes(xKey, xAttributes);
doTest();
}