Merge branch 'master' of git.labs.intellij.net:idea/ultimate

This commit is contained in:
Ekaterina Tuzova
2011-01-27 18:17:56 +03:00
34 changed files with 277 additions and 29 deletions
@@ -24,6 +24,7 @@
<backspaceHandlerDelegate implementation="com.jetbrains.python.editor.PythonBackspaceHandler"/>
<quoteHandler fileType="Python" className="com.jetbrains.python.editor.PythonQuoteHandler"/>
<enterHandlerDelegate implementation="com.jetbrains.python.editor.PythonEnterHandler"/>
<enterHandlerDelegate implementation="com.jetbrains.python.editor.PyEnterBetweenBracketsHandler"/>
<sdkType implementation="com.jetbrains.python.sdk.PythonSdkType"/>
<gotoClassContributor implementation="com.jetbrains.python.PyGotoClassContributor"/>
<gotoSymbolContributor implementation="com.jetbrains.python.PyGotoSymbolContributor"/>
@@ -0,0 +1,13 @@
package com.jetbrains.python.editor;
import com.intellij.codeInsight.editorActions.enter.EnterBetweenBracesHandler;
/**
* @author yole
*/
public class PyEnterBetweenBracketsHandler extends EnterBetweenBracesHandler {
@Override
protected boolean isBracePair(char c1, char c2) {
return c1 == '[' && c2 == ']';
}
}
@@ -7,6 +7,7 @@ import com.intellij.openapi.actionSystem.DataContext;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.actionSystem.EditorActionHandler;
import com.intellij.openapi.editor.actions.SplitLineAction;
import com.intellij.openapi.util.Ref;
import com.intellij.psi.*;
import com.intellij.psi.util.PsiTreeUtil;
@@ -46,6 +47,10 @@ public class PythonEnterHandler implements EnterHandlerDelegate {
if (!(file instanceof PyFile)) {
return Result.Continue;
}
final Boolean isSplitLine = DataManager.getInstance().loadFromDataContext(dataContext, SplitLineAction.SPLIT_LINE_KEY);
if (isSplitLine != null) {
return Result.Continue;
}
Document doc = editor.getDocument();
PsiDocumentManager.getInstance(file.getProject()).commitDocument(doc);
final int offset = caretOffset.get();
@@ -129,7 +129,7 @@ public class PyBlock implements ASTBlock {
!ourBrackets.contains(childType)) {
wrap = Wrap.createWrap(WrapType.NORMAL, true);
}
if (needListAlignment(child)) {
if (needListAlignment(child) && !isEmptyList(_node.getPsi())) {
childAlignment = getAlignmentForChildren();
}
}
@@ -149,7 +149,7 @@ public class PyBlock implements ASTBlock {
childIndent = Indent.getNoneIndent();
}
else {
childIndent = Indent.getContinuationIndent();
childIndent = Indent.getNormalIndent();
}
}
else if (parentType == PyElementTypes.DICT_LITERAL_EXPRESSION) {
@@ -171,6 +171,12 @@ public class PyBlock implements ASTBlock {
childAlignment = getAlignmentForChildren();
}
}
else if (parentType == PyElementTypes.KEY_VALUE_EXPRESSION) {
PyKeyValueExpression keyValue = (PyKeyValueExpression) _node.getPsi();
if (keyValue != null && child.getPsi() == keyValue.getValue()) {
childIndent = Indent.getNormalIndent();
}
}
if (isAfterStatementList(child) && !hasLineBreaksBefore(child, 2)) { // maybe enter was pressed and cut us from a previous (nested) statement list
childIndent = Indent.getNormalIndent();
@@ -179,6 +185,16 @@ public class PyBlock implements ASTBlock {
return new PyBlock(child, childAlignment, childIndent, wrap, mySettings);
}
private static boolean isEmptyList(PsiElement psi) {
if (psi instanceof PyDictLiteralExpression) {
return ((PyDictLiteralExpression) psi).getElements().length == 0;
}
if (psi instanceof PySequenceExpression) {
return ((PySequenceExpression) psi).getElements().length == 0;
}
return false;
}
private static boolean isAfterStatementList(ASTNode child) {
try {
PsiElement prev = sure(child.getPsi().getPrevSibling());
@@ -193,17 +209,33 @@ public class PyBlock implements ASTBlock {
}
}
private static boolean needListAlignment(ASTNode child) {
private boolean needListAlignment(ASTNode child) {
IElementType childType = child.getElementType();
if (PyTokenTypes.OPEN_BRACES.contains(childType)) {
return false;
}
if (PyTokenTypes.CLOSE_BRACES.contains(childType)) {
return PsiTreeUtil.getParentOfType(child.getPsi(), PyArgumentList.class) != null;
ASTNode prevNonSpace = findPrevNonSpaceNode(child);
if (prevNonSpace != null && prevNonSpace.getElementType() == PyTokenTypes.COMMA) {
return true;
}
return false;
}
if (_node.getElementType() == PyElementTypes.ARGUMENT_LIST) {
PyArgumentList argList = (PyArgumentList) _node.getPsi();
return argList != null && argList.getArguments().length > 1;
}
return true;
}
@Nullable
private static ASTNode findPrevNonSpaceNode(ASTNode node) {
do {
node = node.getTreePrev();
} while(node != null && (node.getElementType() == TokenType.WHITE_SPACE || PyTokenTypes.WHITESPACE.contains(node.getElementType())));
return node;
}
private static boolean hasLineBreaksBefore(ASTNode child, int minCount) {
return isWhitespaceWithLineBreaks(TreeUtil.findLastLeaf(child.getTreePrev()), minCount) ||
isWhitespaceWithLineBreaks(child.getFirstChildNode(), minCount);
@@ -263,8 +295,17 @@ public class PyBlock implements ASTBlock {
IElementType type2 = childNode2.getElementType();
if (type1 == PyElementTypes.CLASS_DECLARATION) {
if ((type2 == PyElementTypes.CLASS_DECLARATION || type2 == PyElementTypes.FUNCTION_DECLARATION) &&
parentType instanceof PyFileElementType) {
return getBlankLinesForOption(getPySettings().BLANK_LINES_BETWEEN_TOP_LEVEL_CLASSES_FUNCTIONS);
}
return getBlankLinesForOption(mySettings.BLANK_LINES_AROUND_CLASS);
}
if (type1 == PyElementTypes.FUNCTION_DECLARATION &&
(type2 == PyElementTypes.CLASS_DECLARATION || type2 == PyElementTypes.FUNCTION_DECLARATION) &&
parentType instanceof PyFileElementType) {
return getBlankLinesForOption(getPySettings().BLANK_LINES_BETWEEN_TOP_LEVEL_CLASSES_FUNCTIONS);
}
if (type1 == PyElementTypes.FUNCTION_DECLARATION ||
(type2 == PyElementTypes.FUNCTION_DECLARATION && isStatementOrDeclaration(type1))) {
return getBlankLinesForOption(mySettings.BLANK_LINES_AROUND_METHOD);
@@ -386,7 +427,7 @@ public class PyBlock implements ASTBlock {
return null;
}
private boolean isImportStatement(IElementType type1) {
private static boolean isImportStatement(IElementType type1) {
return (type1 == PyElementTypes.IMPORT_STATEMENT || type1 == PyElementTypes.FROM_IMPORT_STATEMENT);
}
@@ -486,7 +527,9 @@ public class PyBlock implements ASTBlock {
*/
return new ChildAttributes(getChildIndent(newChildIndex), getChildAlignment());
Indent childIndent = getChildIndent(newChildIndex);
Alignment childAlignment = getChildAlignment();
return new ChildAttributes(childIndent, childAlignment);
}
private static boolean dedentAfterLastStatement(PyStatementList statementList) {
@@ -498,25 +541,31 @@ public class PyBlock implements ASTBlock {
return last instanceof PyReturnStatement || last instanceof PyRaiseStatement || last instanceof PyPassStatement;
}
@Nullable
private Alignment getChildAlignment() {
if (ourListElementTypes.contains(_node.getElementType())) {
if (_node.getPsi() instanceof PyDictLiteralExpression) {
PyKeyValueExpression[] elements = ((PyDictLiteralExpression)_node.getPsi()).getElements();
if (elements.length == 0) {
return null;
}
PyKeyValueExpression last = elements[elements.length-1];
if (last.getValue() == null) { // incomplete
return null;
}
}
return getAlignmentForChildren();
}
return null;
}
private Indent getChildIndent(int newChildIndex) {
ASTNode afterNode = getAfterNode(newChildIndex);
ASTNode lastChild = getLastNonSpaceChild(_node, false);
if (lastChild != null && lastChild.getElementType() == PyElementTypes.STATEMENT_LIST && _subBlocks.size() >= newChildIndex) {
if (newChildIndex == 0) { // block text contains backslash line wrappings, child block list not built
if (afterNode == null) {
return Indent.getNoneIndent();
}
int prevIndex = newChildIndex - 1;
while (prevIndex > 0 && _subBlocks.get(prevIndex).getNode().getElementType() == PyTokenTypes.END_OF_LINE_COMMENT) {
prevIndex--;
}
PyBlock insertAfterBlock = _subBlocks.get(prevIndex);
ASTNode afterNode = insertAfterBlock.getNode();
// handle pressing Enter after colon and before first statement in
// existing statement list
@@ -570,6 +619,13 @@ public class PyBlock implements ASTBlock {
}
}
if (afterNode != null && afterNode.getElementType() == PyElementTypes.KEY_VALUE_EXPRESSION) {
PyKeyValueExpression keyValue = (PyKeyValueExpression) afterNode.getPsi();
if (keyValue != null && keyValue.getValue() == null) { // incomplete
return Indent.getContinuationIndent(true);
}
}
// constructs that imply indent for their children
if (ourListElementTypes.contains(_node.getElementType()) || _node.getPsi() instanceof PyStatementPart) {
return Indent.getNormalIndent();
@@ -589,6 +645,19 @@ public class PyBlock implements ASTBlock {
*/
}
@Nullable
private ASTNode getAfterNode(int newChildIndex) {
if (newChildIndex == 0) { // block text contains backslash line wrappings, child block list not built
return null;
}
int prevIndex = newChildIndex - 1;
while (prevIndex > 0 && _subBlocks.get(prevIndex).getNode().getElementType() == PyTokenTypes.END_OF_LINE_COMMENT) {
prevIndex--;
}
PyBlock insertAfterBlock = _subBlocks.get(prevIndex);
return insertAfterBlock.getNode();
}
private static ASTNode getLastNonSpaceChild(ASTNode node, boolean acceptError) {
ASTNode lastChild = node.getLastChildNode();
while (lastChild != null &&
@@ -14,6 +14,8 @@ public class PyCodeStyleSettings extends CustomCodeStyleSettings {
public boolean SPACE_AROUND_EQ_IN_NAMED_PARAMETER = false;
public boolean SPACE_AROUND_EQ_IN_KEYWORD_ARGUMENT = false;
public int BLANK_LINES_BETWEEN_TOP_LEVEL_CLASSES_FUNCTIONS = 2;
public PyCodeStyleSettings(CodeStyleSettings container) {
super("Python", container);
}
@@ -57,6 +57,8 @@ public class PyLanguageCodeStyleSettingsProvider extends LanguageCodeStyleSettin
"BLANK_LINES_AFTER_IMPORTS",
"KEEP_BLANK_LINES_IN_DECLARATIONS",
"KEEP_BLANK_LINES_IN_CODE");
consumer.showCustomOption(PyCodeStyleSettings.class, "BLANK_LINES_BETWEEN_TOP_LEVEL_CLASSES_FUNCTIONS", "Between top-level classes and functions",
BLANK_LINES);
}
else if (settingsType == SettingsType.WRAPPING_AND_BRACES_SETTINGS) {
consumer.showAllStandardOptions();
@@ -188,7 +188,10 @@ public class ExpressionParsing extends Parsing {
private void parseDictLiteralTail(PsiBuilder.Marker startMarker, PsiBuilder.Marker firstKeyValueMarker) {
if (!parseSingleExpression(false)) {
myBuilder.error("expression expected");
firstKeyValueMarker.drop();
firstKeyValueMarker.done(PyElementTypes.KEY_VALUE_EXPRESSION);
if (atToken(PyTokenTypes.RBRACE)) {
myBuilder.advanceLexer();
}
startMarker.done(PyElementTypes.DICT_LITERAL_EXPRESSION);
return;
}
@@ -308,10 +311,14 @@ public class ExpressionParsing extends Parsing {
parseSliceEnd(expr, sliceItemStart);
}
else {
parseExpressionOptional();
if (myBuilder.getTokenType() == PyTokenTypes.COLON) {
parseSingleExpression(false);
if (atToken(PyTokenTypes.COLON)) {
parseSliceEnd(expr, sliceItemStart);
}
else if (atToken(PyTokenTypes.COMMA)) {
sliceItemStart.done(PyElementTypes.SLICE_ITEM);
parseSliceListTail(expr);
}
else {
sliceItemStart.drop();
checkMatches(PyTokenTypes.RBRACKET, message("PARSE.expected.rbracket"));
@@ -373,9 +380,13 @@ public class ExpressionParsing extends Parsing {
}
}
parseSliceListTail(exprStart);
}
private void parseSliceListTail(PsiBuilder.Marker exprStart) {
while (atToken(PyTokenTypes.COMMA)) {
nextToken();
sliceItemStart = myBuilder.mark();
PsiBuilder.Marker sliceItemStart = myBuilder.mark();
parseTestExpression(false, false);
if (matchToken(PyTokenTypes.COLON)) {
parseTestExpression(false, false);
@@ -1,6 +1,5 @@
class C:
pass
def foo():
pass
def bar():
pass
def foo(self):
pass
def bar(self):
pass
@@ -1,8 +1,6 @@
class C:
pass
def foo(self):
pass
def foo():
pass
def bar():
pass
def bar(self):
pass
@@ -0,0 +1 @@
a[b1,:]
@@ -0,0 +1 @@
a[b1, :]
@@ -0,0 +1,5 @@
class C:
pass
class D:
pass
@@ -0,0 +1,6 @@
class C:
pass
class D:
pass
@@ -0,0 +1,11 @@
class D:
pass
def foo():
pass
def bar():
pass
class C:
pass
@@ -0,0 +1,14 @@
class D:
pass
def foo():
pass
def bar():
pass
class C:
pass
@@ -1,6 +1,7 @@
class Xyzzy(object):
pass
class XyzzyTest:
def testSimple(self):
c = Xyzzy()
+1
View File
@@ -0,0 +1 @@
some_dict = { 'key': }
+18
View File
@@ -0,0 +1,18 @@
PyFile:IncompleteDict.py
PyAssignmentStatement
PyTargetExpression: some_dict
PsiElement(Py:IDENTIFIER)('some_dict')
PsiWhiteSpace(' ')
PsiElement(Py:EQ)('=')
PsiWhiteSpace(' ')
PyDictLiteralExpression
PsiElement(Py:LBRACE)('{')
PsiWhiteSpace(' ')
PyKeyValueExpression
PyStringLiteralExpression: key
PsiElement(Py:STRING_LITERAL)(''key'')
PsiElement(Py:COLON)(':')
PsiErrorElement:expression expected
<empty list>
PsiWhiteSpace(' ')
PsiElement(Py:RBRACE)('}')
+1
View File
@@ -0,0 +1 @@
a[b1,:]
+13
View File
@@ -0,0 +1,13 @@
PyFile:SliceList.py
PyExpressionStatement
PySliceExpression
PyReferenceExpression: a
PsiElement(Py:IDENTIFIER)('a')
PsiElement(Py:LBRACKET)('[')
PySliceItem
PyReferenceExpression: b1
PsiElement(Py:IDENTIFIER)('b1')
PsiElement(Py:COMMA)(',')
PySliceItem
PsiElement(Py:COLON)(':')
PsiElement(Py:RBRACKET)(']')
@@ -2,5 +2,6 @@
def bar():
print("Hello")
def foo():
bar()
@@ -2,6 +2,7 @@ def bar(base_new, self_new):
try: base_new.__init__(self_new)
except AttributeError: pass
def __init__(self):
for base in self__class__.__bases__:
bar(base, self)
@@ -1,5 +1,6 @@
def bar(p_name_new, params_new):
return p_name_new + '(' + ', '.join(params_new) + ')'
def x(p_name, params):
return bar(p_name, params), None
@@ -2,6 +2,7 @@ class Suppa:
def foo(self):
print "bar"
class Foo(Suppa):
def bar(self):
print "foo"
@@ -2,6 +2,7 @@ class Suppa(object):
def foo(self):
print "bar"
class Foo(Suppa):
def bar(self):
print "foo"
@@ -5,5 +5,6 @@ class Foo:
def boo(self):
print "rrrrr"
class Boo(Foo):
pass
@@ -6,5 +6,6 @@ class Foo:
def boo(self):
print "rrrrr"
class Boo(Foo):
pass
@@ -5,6 +5,7 @@ class Foo:
def boo(self):
print "rrrrr"
class Boo(Foo):
'''
this is boo
@@ -1,6 +1,7 @@
class Foo:
pass
class Zope(Foo):
def _mine(self):
print "zope"
@@ -8,6 +9,7 @@ class Zope(Foo):
def foo(self):
print("a")
class Boo(Foo):
def boo(self):
print "rrrrr"
@@ -1,6 +1,7 @@
class A:
pass
class B(A):
def meth_b1(self):
pass
@@ -13,6 +14,7 @@ class B(A):
def meth_a2(self):
pass
class D(A):
def meth_d1(self):
pass
@@ -1,6 +1,7 @@
class Foo:
pass
class Boo(Foo):
def boo(self):
print "rrrrr"
@@ -92,6 +92,18 @@ public class PyFormatterTest extends PyLightFixtureTestCase {
doTest();
}
public void testTwoLinesBetweenTopLevelClasses() { // PY-2765
doTest();
}
public void testTwoLinesBetweenTopLevelFunctions() { // PY-2765
doTest();
}
public void testSpecialSlice() { // PY-1928
doTest();
}
private void doTest() {
myFixture.configureByFile("formatter/" + getTestName(true) + ".py");
ApplicationManager.getApplication().runWriteAction(new Runnable() {
@@ -71,12 +71,49 @@ public class PyIndentTest extends PyLightFixtureTestCase {
doTest("__all__ = [a for<caret>", "__all__ = [a for\n" + " <caret>");
}
public void testAlignInListOnceMore() { // PY-2407
doTest("for id in [\"SEARCH_RESULT_ATTRIBUTES\", \n" +
" \"WRITE_SEARCH_RESULT_ATTRIBUTES\", \n" +
" \"IDENTIFIER_UNDER_CARET_ATTRIBUTES\",<caret>]:",
"for id in [\"SEARCH_RESULT_ATTRIBUTES\", \n" +
" \"WRITE_SEARCH_RESULT_ATTRIBUTES\", \n" +
" \"IDENTIFIER_UNDER_CARET_ATTRIBUTES\",\n" +
" <caret>]:");
}
public void testAlignInDict() {
doTest("some_call({'aaa': 'v1',<caret>})",
"some_call({'aaa': 'v1',\n" +
" <caret>})");
}
public void testAlignInDictInParams() { // PY-1947
doTest("foobar({<caret>})",
"foobar({\n" +
" <caret>\n" +
"})");
}
public void testIndentDictMissingValue() { // PY-1469
doTest("some_dict = {\n" +
" 'key': <caret>\n" +
"}",
"some_dict = {\n" +
" 'key': \n" +
" <caret>\n" +
"}");
}
public void testIndentDictStringValue() { // PY-1469
doTest("some_dict = {\n" +
" 'key': <caret>''\n" +
"}",
"some_dict = {\n" +
" 'key': \n" +
" <caret>''\n" +
"}");
}
public void testClass() {
doTest("class A:\n" + " print a<caret>", "class A:\n" + " print a\n" + " <caret>");
}
@@ -128,7 +165,7 @@ public class PyIndentTest extends PyLightFixtureTestCase {
}
public void testEnterInEmptyList() {
doTest("[<caret>]", "[\n]");
doTest("[<caret>]", "[\n <caret>\n]");
}
public void testEnterInEmptyDict() {
@@ -149,6 +186,12 @@ public class PyIndentTest extends PyLightFixtureTestCase {
")");
}
public void testEnterInNonEmptyArgList() { // PY-1947
doTest("Task(<caret>params=1)",
"Task(\n" +
" <caret>params=1)");
}
public void testIndentAfterComment() { // PY-641
doTest("def foo():\n #some_call()<caret>\n another_call()", "def foo():\n #some_call()\n <caret>\n another_call()");
}
@@ -239,6 +239,14 @@ public class PythonParsingTest extends ParsingTestCase {
doTest();
}
public void testIncompleteDict() {
doTest();
}
public void testSliceList() { // PY-1928
doTest();
}
public void doTest() {
doTest(LanguageLevel.PYTHON25);
}