diff --git a/grammar/src/control.grm b/grammar/src/control.grm index f6616ac4446..bbcd4f95e04 100644 --- a/grammar/src/control.grm +++ b/grammar/src/control.grm @@ -1,5 +1,5 @@ if - : "if" "(" expression ")" expression ("else" expression)? + : "if" "(" expression ")" expression SEMI? ("else" expression)? ; try diff --git a/grammar/src/expressions.grm b/grammar/src/expressions.grm index ab1b43111cc..16eaacf272d 100644 --- a/grammar/src/expressions.grm +++ b/grammar/src/expressions.grm @@ -211,7 +211,7 @@ functionLiteral // one can use "it" as a parameter name ; expressions - : expression{SEMI} SEMI? + : SEMI* expression{SEMI+} SEMI* ; constructorInvocation diff --git a/idea/src/org/jetbrains/jet/lang/parsing/JetExpressionParsing.java b/idea/src/org/jetbrains/jet/lang/parsing/JetExpressionParsing.java index f1765f0e9f7..e72e86402cd 100644 --- a/idea/src/org/jetbrains/jet/lang/parsing/JetExpressionParsing.java +++ b/idea/src/org/jetbrains/jet/lang/parsing/JetExpressionParsing.java @@ -298,6 +298,7 @@ public class JetExpressionParsing extends AbstractJetParsing { PsiBuilder.Marker indices = mark(); + myBuilder.disableEols(); advance(); // LBRACKET while (true) { @@ -312,6 +313,7 @@ public class JetExpressionParsing extends AbstractJetParsing { } expect(RBRACKET, "Expecting ']'"); + myBuilder.restoreEolsState(); indices.done(INDICES); } @@ -357,6 +359,7 @@ public class JetExpressionParsing extends AbstractJetParsing { PsiBuilder.Marker literal = mark(); + myBuilder.enableEols(); advance(); // LBRACE int doubleArrowPos = matchTokenStreamPredicate(new FirstBefore(new At(DOUBLE_ARROW), new At(RBRACE)) { @@ -430,6 +433,7 @@ public class JetExpressionParsing extends AbstractJetParsing { body.done(BODY); expect(RBRACE, "Expecting '}'"); + myBuilder.restoreEolsState(); literal.done(FUNCTION_LITERAL); } @@ -441,6 +445,8 @@ public class JetExpressionParsing extends AbstractJetParsing { PsiBuilder.Marker list = mark(); expect(LPAR, "Expecting a parameter list in parentheses (...)", TokenSet.create(DOUBLE_ARROW, COLON)); + myBuilder.disableEols(); + if (!at(RPAR)) { while (true) { if (at(COMMA)) errorAndAdvance("Expecting a parameter declaration"); @@ -466,19 +472,22 @@ public class JetExpressionParsing extends AbstractJetParsing { } } + myBuilder.restoreEolsState(); + expect(RPAR, "Expecting ')", TokenSet.create(DOUBLE_ARROW, COLON)); list.done(VALUE_PARAMETER_LIST); } /* * expressions - * : expression{SEMI} SEMI? + * : SEMI* expression{SEMI+} SEMI* */ public void parseExpressions() { + while (at(SEMICOLON)) advance(); // SEMICOLON while (!eof() && !at(RBRACE)) { parseExpression(); if (at(SEMICOLON)) { - advance(); // SEMICOLON + while (at(SEMICOLON)) advance(); // SEMICOLON } else if (at(RBRACE)) { break; } else if (!myBuilder.eolInLastWhitespace()) { @@ -532,7 +541,9 @@ public class JetExpressionParsing extends AbstractJetParsing { advance(); // DO_KEYWORD - parseControlStructureBody(); + if (!at(WHILE_KEYWORD)) { + parseControlStructureBody(); + } expect(WHILE_KEYWORD, "Expecting 'while' followed by a post-condition"); @@ -572,6 +583,7 @@ public class JetExpressionParsing extends AbstractJetParsing { advance(); // FOR_KEYWORD + myBuilder.disableEols(); expect(LPAR, "Expecting '(' to open a loop range", TokenSet.create(RPAR, VAL_KEYWORD, VAR_KEYWORD, IDENTIFIER)); PsiBuilder.Marker parameter = mark(); @@ -590,6 +602,7 @@ public class JetExpressionParsing extends AbstractJetParsing { range.done(LOOP_RANGE); expectNoAdvance(RPAR, "Expecting ')'"); + myBuilder.restoreEolsState(); parseControlStructureBody(); @@ -601,10 +614,9 @@ public class JetExpressionParsing extends AbstractJetParsing { */ private void parseControlStructureBody() { PsiBuilder.Marker body = mark(); - parseExpression(); + if (!at(SEMICOLON)) + parseExpression(); body.done(BODY); - - // TODO: empty body? } /* @@ -653,7 +665,7 @@ public class JetExpressionParsing extends AbstractJetParsing { /* * if - * : "if" "(" expression ")" expression ("else" expression)? + * : "if" "(" expression ")" expression SEMI? ("else" expression)? * ; */ private void parseIf() { @@ -665,16 +677,20 @@ public class JetExpressionParsing extends AbstractJetParsing { parseCondition(); - // TODO: empty body? PsiBuilder.Marker thenBranch = mark(); - parseExpression(); + if (!at(ELSE_KEYWORD) && !at(SEMICOLON)) { + parseExpression(); + } + consumeIf(SEMICOLON); thenBranch.done(THEN); if (at(ELSE_KEYWORD)) { advance(); // ELSE_KEYWORD PsiBuilder.Marker elseBranch = mark(); - parseExpression(); + if (!at(SEMICOLON)) { + parseExpression(); + } elseBranch.done(ELSE); } @@ -685,6 +701,7 @@ public class JetExpressionParsing extends AbstractJetParsing { * "(" expression ")" */ private void parseCondition() { + myBuilder.disableEols(); expect(LPAR, "Expecting a condition in parentheses '(...)'"); PsiBuilder.Marker condition = mark(); @@ -692,6 +709,7 @@ public class JetExpressionParsing extends AbstractJetParsing { condition.done(CONDITION); expect(RPAR, "Expecting ')"); + myBuilder.restoreEolsState(); } /* @@ -769,23 +787,15 @@ public class JetExpressionParsing extends AbstractJetParsing { PsiBuilder.Marker typeof = mark(); advance(); // TYPEOF_KEYWORD + myBuilder.disableEols(); expect(LPAR, "Expecting '('"); - parseExpression(); - expect(RPAR, "Expecting ')'"); - typeof.done(TYPEOF); - } - /* - * mapLiteralEntry - * : expression ":" expression - * ; - */ - private void parseMapLiteralEntry() { - PsiBuilder.Marker entry = mark(); parseExpression(); - expect(COLON, "Expecting ':'"); - parseExpression(); - entry.done(MAP_LITERAL_ENTRY); + + expect(RPAR, "Expecting ')'"); + myBuilder.restoreEolsState(); + + typeof.done(TYPEOF); } /* @@ -798,8 +808,10 @@ public class JetExpressionParsing extends AbstractJetParsing { PsiBuilder.Marker mark = mark(); + myBuilder.disableEols(); advance(); // LPAR + while (true) { if (at(COMMA)) errorAndAdvance("Expecting a tuple entry (expression)"); parseExpression(); @@ -808,6 +820,7 @@ public class JetExpressionParsing extends AbstractJetParsing { } expect(RPAR, "Expecting ')'"); + myBuilder.restoreEolsState(); mark.done(TUPLE); } @@ -822,6 +835,7 @@ public class JetExpressionParsing extends AbstractJetParsing { // This may be "this < foo" or "this", thus the backtracking PsiBuilder.Marker supertype = mark(); + myBuilder.disableEols(); advance(); // LT myJetParsing.parseTypeRef(); @@ -833,6 +847,7 @@ public class JetExpressionParsing extends AbstractJetParsing { else { supertype.rollbackTo(); } + myBuilder.restoreEolsState(); } } @@ -844,6 +859,7 @@ public class JetExpressionParsing extends AbstractJetParsing { public void parseValueArgumentList() { PsiBuilder.Marker list = mark(); + myBuilder.disableEols(); expect(LPAR, "Expecting a parameter list", TokenSet.create(RPAR)); if (!at(RPAR)) { @@ -855,6 +871,7 @@ public class JetExpressionParsing extends AbstractJetParsing { } expect(RPAR, "Expecting ')'"); + myBuilder.restoreEolsState(); list.done(VALUE_ARGUMENT_LIST); } diff --git a/idea/src/org/jetbrains/jet/lang/parsing/JetParsing.java b/idea/src/org/jetbrains/jet/lang/parsing/JetParsing.java index f70b1a02e45..77eb34b705d 100644 --- a/idea/src/org/jetbrains/jet/lang/parsing/JetParsing.java +++ b/idea/src/org/jetbrains/jet/lang/parsing/JetParsing.java @@ -239,6 +239,7 @@ public class JetParsing extends AbstractJetParsing { PsiBuilder.Marker properties = mark(); + myBuilder.disableEols(); expect(LPAR, "Expecting a property list in parentheses '( ... )'"); // Property list @@ -259,6 +260,7 @@ public class JetParsing extends AbstractJetParsing { } expect(RPAR, "Expecting ')' to close a property list"); + myBuilder.restoreEolsState(); consumeIf(SEMICOLON); @@ -314,6 +316,7 @@ public class JetParsing extends AbstractJetParsing { assert at(LBRACKET); PsiBuilder.Marker annotation = mark(); + myBuilder.disableEols(); advance(); // LBRACKET while (true) { @@ -329,6 +332,7 @@ public class JetParsing extends AbstractJetParsing { } expect(RBRACKET, "Expecting ']' to close an attribute annotation"); + myBuilder.restoreEolsState(); annotation.done(ATTRIBUTE_ANNOTATION); } @@ -370,6 +374,7 @@ public class JetParsing extends AbstractJetParsing { return NAMESPACE; } + myBuilder.enableEols(); advance(); // LBRACE PsiBuilder.Marker namespaceBody = mark(); @@ -377,6 +382,7 @@ public class JetParsing extends AbstractJetParsing { namespaceBody.done(NAMESPACE_BODY); expect(RBRACE, "Expecting '}'"); + myBuilder.restoreEolsState(); return NAMESPACE; } @@ -430,6 +436,7 @@ public class JetParsing extends AbstractJetParsing { PsiBuilder.Marker classBody = mark(); + myBuilder.enableEols(); advance(); // LBRACE while (!eof() && !at(RBRACE)) { @@ -459,6 +466,7 @@ public class JetParsing extends AbstractJetParsing { } expect(RBRACE, "Expecting '}' to close enum class body"); + myBuilder.restoreEolsState(); classBody.done(CLASS_BODY); } @@ -500,6 +508,8 @@ public class JetParsing extends AbstractJetParsing { /*package*/ void parseClassBody() { assert at(LBRACE); PsiBuilder.Marker body = mark(); + + myBuilder.enableEols(); advance(); // LBRACE while (!eof()) { @@ -509,6 +519,8 @@ public class JetParsing extends AbstractJetParsing { parseMemberDeclaration(); } expect(RBRACE, "Missing '}"); + myBuilder.restoreEolsState(); + body.done(CLASS_BODY); } @@ -766,6 +778,7 @@ public class JetParsing extends AbstractJetParsing { boolean setter = at(SET_KEYWORD); advance(); // GET_KEYWORD or SET_KEYWORD + myBuilder.disableEols(); expect(LPAR, "Expecting '('", TokenSet.create(RPAR, IDENTIFIER, COLON, LBRACE, EQ)); if (setter) { PsiBuilder.Marker setterParameter = mark(); @@ -782,6 +795,7 @@ public class JetParsing extends AbstractJetParsing { } if (!at(RPAR)) errorUntil("Expecting ')'", TokenSet.create(RPAR, COLON, LBRACE, EQ, EOL_OR_SEMICOLON)); expect(RPAR, "Expecting ')'", TokenSet.create(RPAR, COLON, LBRACE, EQ)); + myBuilder.restoreEolsState(); if (at(COLON)) { advance(); @@ -827,7 +841,6 @@ public class JetParsing extends AbstractJetParsing { parseTypeParameterList(TokenSet.orSet(TokenSet.create(LPAR), valueParametersFollow)); - parseValueParameterList(false, valueParametersFollow); if (at(COLON)) { @@ -874,11 +887,13 @@ public class JetParsing extends AbstractJetParsing { public void parseBlock() { PsiBuilder.Marker block = mark(); + myBuilder.enableEols(); expect(LBRACE, "Expecting '{' to open a block"); myExpressionParsing.parseExpressions(); expect(RBRACE, "Expecting '}"); + myBuilder.restoreEolsState(); block.done(BLOCK); } @@ -981,6 +996,8 @@ public class JetParsing extends AbstractJetParsing { private void parsePrimaryConstructorParameterList() { assert at(LPAR); PsiBuilder.Marker cons = mark(); + + myBuilder.disableEols(); advance(); // LPAR while (true) { @@ -990,6 +1007,7 @@ public class JetParsing extends AbstractJetParsing { } expect(RPAR, "')' expected"); + myBuilder.restoreEolsState(); cons.done(PRIMARY_CONSTRUCTOR_PARAMETERS_LIST); } @@ -1047,6 +1065,8 @@ public class JetParsing extends AbstractJetParsing { private void parseTypeParameterList(TokenSet recoverySet) { PsiBuilder.Marker list = mark(); if (at(LT)) { + + myBuilder.disableEols(); advance(); // LT while (true) { @@ -1058,6 +1078,7 @@ public class JetParsing extends AbstractJetParsing { } expect(GT, "Missing '>'", recoverySet); + myBuilder.restoreEolsState(); if (at(WHERE_KEYWORD)) { parseTypeConstraintList(); @@ -1199,6 +1220,7 @@ public class JetParsing extends AbstractJetParsing { PsiBuilder.Marker list = mark(); + myBuilder.disableEols(); advance(); // LT while (true) { @@ -1209,6 +1231,7 @@ public class JetParsing extends AbstractJetParsing { } expect(GT, "Expecting a '>'"); + myBuilder.restoreEolsState(); list.done(TYPE_ARGUMENT_LIST); } @@ -1224,6 +1247,7 @@ public class JetParsing extends AbstractJetParsing { PsiBuilder.Marker tuple = mark(); + myBuilder.disableEols(); advance(); // LPAR if (!at(RPAR)) { @@ -1252,6 +1276,7 @@ public class JetParsing extends AbstractJetParsing { } expect(RPAR, "Expecting ')"); + myBuilder.restoreEolsState(); tuple.done(TUPLE_TYPE); } @@ -1266,6 +1291,7 @@ public class JetParsing extends AbstractJetParsing { PsiBuilder.Marker functionType = mark(); + myBuilder.disableEols(); advance(); // LBRACE int lastLPar = findLastBefore(TokenSet.create(LPAR), TokenSet.create(RBRACE, COLON), false); @@ -1280,6 +1306,8 @@ public class JetParsing extends AbstractJetParsing { parseFunctionTypeContents(); expect(RBRACE, "Expecting '}"); + myBuilder.restoreEolsState(); + functionType.done(FUNCTION_TYPE); } @@ -1311,6 +1339,8 @@ public class JetParsing extends AbstractJetParsing { */ public void parseValueParameterList(boolean isFunctionTypeContents, TokenSet recoverySet) { PsiBuilder.Marker parameters = mark(); + + myBuilder.disableEols(); expect(LPAR, "Expecting '(", recoverySet); if (!at(RPAR)) { @@ -1335,6 +1365,8 @@ public class JetParsing extends AbstractJetParsing { } } expect(RPAR, "Expecting ')'", recoverySet); + myBuilder.restoreEolsState(); + parameters.done(VALUE_PARAMETER_LIST); } diff --git a/idea/src/org/jetbrains/jet/lang/parsing/SemanticWhitespaceAwarePsiBuilder.java b/idea/src/org/jetbrains/jet/lang/parsing/SemanticWhitespaceAwarePsiBuilder.java index c5bf1843936..7815ed8e842 100644 --- a/idea/src/org/jetbrains/jet/lang/parsing/SemanticWhitespaceAwarePsiBuilder.java +++ b/idea/src/org/jetbrains/jet/lang/parsing/SemanticWhitespaceAwarePsiBuilder.java @@ -7,4 +7,7 @@ import com.intellij.lang.PsiBuilder; */ public interface SemanticWhitespaceAwarePsiBuilder extends PsiBuilder { boolean eolInLastWhitespace(); + void disableEols(); + void enableEols(); + void restoreEolsState(); } diff --git a/idea/src/org/jetbrains/jet/lang/parsing/SemanticWhitespaceAwarePsiBuilderImpl.java b/idea/src/org/jetbrains/jet/lang/parsing/SemanticWhitespaceAwarePsiBuilderImpl.java index 29edce42e3f..765b61a742c 100644 --- a/idea/src/org/jetbrains/jet/lang/parsing/SemanticWhitespaceAwarePsiBuilderImpl.java +++ b/idea/src/org/jetbrains/jet/lang/parsing/SemanticWhitespaceAwarePsiBuilderImpl.java @@ -5,6 +5,8 @@ import com.intellij.lang.PsiBuilder; import com.intellij.lang.WhitespaceSkippedCallback; import com.intellij.psi.tree.IElementType; +import java.util.Stack; + /** * @author abreslav */ @@ -23,10 +25,12 @@ public class SemanticWhitespaceAwarePsiBuilderImpl extends PsiBuilderAdapter imp }; private boolean myEOLInLastWhitespace; + private final Stack myEOLsEnabled = new Stack(); public SemanticWhitespaceAwarePsiBuilderImpl(final PsiBuilder delegate) { super(delegate); delegate.setWhitespaceSkippedCallback(myWhitespaceSkippedCallback); + myEOLsEnabled.push(true); } @Override @@ -37,7 +41,23 @@ public class SemanticWhitespaceAwarePsiBuilderImpl extends PsiBuilderAdapter imp @Override public boolean eolInLastWhitespace() { - return myEOLInLastWhitespace || eof(); + return myEOLsEnabled.peek() && (myEOLInLastWhitespace || eof()); + } + + @Override + public void disableEols() { + myEOLsEnabled.push(false); + } + + @Override + public void enableEols() { + myEOLsEnabled.push(true); + } + + @Override + public void restoreEolsState() { + assert myEOLsEnabled.size() > 1; + myEOLsEnabled.pop(); } // TODO: Overhead diff --git a/idea/src/org/jetbrains/jet/lang/parsing/TruncatedSemanticWhitespaceAwarePsiBuilder.java b/idea/src/org/jetbrains/jet/lang/parsing/TruncatedSemanticWhitespaceAwarePsiBuilder.java index 7b9ad549118..010c17e9808 100644 --- a/idea/src/org/jetbrains/jet/lang/parsing/TruncatedSemanticWhitespaceAwarePsiBuilder.java +++ b/idea/src/org/jetbrains/jet/lang/parsing/TruncatedSemanticWhitespaceAwarePsiBuilder.java @@ -22,6 +22,20 @@ public class TruncatedSemanticWhitespaceAwarePsiBuilder extends PsiBuilderAdapte return myBuilder.eolInLastWhitespace(); } + @Override + public void disableEols() { + myBuilder.disableEols();; + } + + @Override + public void enableEols() { + myBuilder.enableEols(); + } + + @Override + public void restoreEolsState() { + myBuilder.restoreEolsState(); + } @Override public boolean eof() { diff --git a/idea/testData/psi/ControlStructures.jet b/idea/testData/psi/ControlStructures.jet index 44cf337e1b7..a9968194fec 100644 --- a/idea/testData/psi/ControlStructures.jet +++ b/idea/testData/psi/ControlStructures.jet @@ -66,13 +66,72 @@ fun a( } for (val x in foo) a - for (x in foo) a - for (val x : Int in foo) a - for (x : Int in foo) {} + for (x in foo) a + for (val x : Int in foo) a + for (x : Int in foo) {} while (true) {} do { } while (false) + +fun foo() { + for (a in b) + b + + for (a in b) {} + + for (a in b) { + b + } + + for (a in b); + b + + while (a in b) + b + + while (a in b) { + b + } + + while (a in b); + b + + while (a) {} + + if (a) + b + else + c + + if (a) b else c + + if (a) b + else c + + if (a) + b; + else + c; + + if (a) b + if (a) + b + if (a) + b; + + if (a) else c + if (a) + else c + if (a) + ; + else c + if (a) + else ; + + do while (r) + do foo while (r) + do {;;;foo;bar;;;; } while (r) } \ No newline at end of file diff --git a/idea/testData/psi/ControlStructures.txt b/idea/testData/psi/ControlStructures.txt index 69f87139e36..725863361d5 100644 --- a/idea/testData/psi/ControlStructures.txt +++ b/idea/testData/psi/ControlStructures.txt @@ -431,7 +431,7 @@ JetFile: ControlStructures.jet PsiWhiteSpace(' ') BODY PsiElement(IDENTIFIER)('a') - PsiWhiteSpace('\n ') + PsiWhiteSpace('\n ') FOR PsiElement(for)('for') PsiWhiteSpace(' ') @@ -447,7 +447,7 @@ JetFile: ControlStructures.jet PsiWhiteSpace(' ') BODY PsiElement(IDENTIFIER)('a') - PsiWhiteSpace('\n ') + PsiWhiteSpace('\n ') FOR PsiElement(for)('for') PsiWhiteSpace(' ') @@ -471,7 +471,7 @@ JetFile: ControlStructures.jet PsiWhiteSpace(' ') BODY PsiElement(IDENTIFIER)('a') - PsiWhiteSpace('\n ') + PsiWhiteSpace('\n ') FOR PsiElement(for)('for') PsiWhiteSpace(' ') @@ -532,5 +532,379 @@ JetFile: ControlStructures.jet BOOLEAN_CONSTANT PsiElement(false)('false') PsiElement(RPAR)(')') - PsiWhiteSpace('\n') - PsiElement(RBRACE)('}') \ No newline at end of file + PsiWhiteSpace('\n\n') + FUN + PsiElement(fun)('fun') + PsiWhiteSpace(' ') + PsiElement(IDENTIFIER)('foo') + TYPE_PARAMETER_LIST + + VALUE_PARAMETER_LIST + PsiElement(LPAR)('(') + PsiElement(RPAR)(')') + PsiWhiteSpace(' ') + BLOCK + PsiElement(LBRACE)('{') + PsiWhiteSpace('\n ') + FOR + PsiElement(for)('for') + PsiWhiteSpace(' ') + PsiElement(LPAR)('(') + LOOP_PARAMETER + PsiElement(IDENTIFIER)('a') + PsiWhiteSpace(' ') + PsiElement(in)('in') + PsiWhiteSpace(' ') + LOOP_RANGE + PsiElement(IDENTIFIER)('b') + PsiElement(RPAR)(')') + PsiWhiteSpace('\n ') + BODY + PsiElement(IDENTIFIER)('b') + PsiWhiteSpace('\n\n ') + FOR + PsiElement(for)('for') + PsiWhiteSpace(' ') + PsiElement(LPAR)('(') + LOOP_PARAMETER + PsiElement(IDENTIFIER)('a') + PsiWhiteSpace(' ') + PsiElement(in)('in') + PsiWhiteSpace(' ') + LOOP_RANGE + PsiElement(IDENTIFIER)('b') + PsiElement(RPAR)(')') + PsiWhiteSpace(' ') + BODY + FUNCTION_LITERAL + PsiElement(LBRACE)('{') + BODY + + PsiElement(RBRACE)('}') + PsiWhiteSpace('\n\n ') + FOR + PsiElement(for)('for') + PsiWhiteSpace(' ') + PsiElement(LPAR)('(') + LOOP_PARAMETER + PsiElement(IDENTIFIER)('a') + PsiWhiteSpace(' ') + PsiElement(in)('in') + PsiWhiteSpace(' ') + LOOP_RANGE + PsiElement(IDENTIFIER)('b') + PsiElement(RPAR)(')') + PsiWhiteSpace(' ') + BODY + FUNCTION_LITERAL + PsiElement(LBRACE)('{') + PsiWhiteSpace('\n ') + BODY + PsiElement(IDENTIFIER)('b') + PsiWhiteSpace('\n ') + PsiElement(RBRACE)('}') + PsiWhiteSpace('\n\n ') + FOR + PsiElement(for)('for') + PsiWhiteSpace(' ') + PsiElement(LPAR)('(') + LOOP_PARAMETER + PsiElement(IDENTIFIER)('a') + PsiWhiteSpace(' ') + PsiElement(in)('in') + PsiWhiteSpace(' ') + LOOP_RANGE + PsiElement(IDENTIFIER)('b') + PsiElement(RPAR)(')') + BODY + + PsiElement(SEMICOLON)(';') + PsiWhiteSpace('\n ') + PsiElement(IDENTIFIER)('b') + PsiWhiteSpace('\n\n ') + WHILE + PsiElement(while)('while') + PsiWhiteSpace(' ') + PsiElement(LPAR)('(') + CONDITION + BINARY_EXPRESSION + PsiElement(IDENTIFIER)('a') + PsiWhiteSpace(' ') + PsiElement(in)('in') + PsiWhiteSpace(' ') + PsiElement(IDENTIFIER)('b') + PsiElement(RPAR)(')') + PsiWhiteSpace('\n ') + BODY + PsiElement(IDENTIFIER)('b') + PsiWhiteSpace('\n\n ') + WHILE + PsiElement(while)('while') + PsiWhiteSpace(' ') + PsiElement(LPAR)('(') + CONDITION + BINARY_EXPRESSION + PsiElement(IDENTIFIER)('a') + PsiWhiteSpace(' ') + PsiElement(in)('in') + PsiWhiteSpace(' ') + PsiElement(IDENTIFIER)('b') + PsiElement(RPAR)(')') + PsiWhiteSpace(' ') + BODY + FUNCTION_LITERAL + PsiElement(LBRACE)('{') + PsiWhiteSpace('\n ') + BODY + PsiElement(IDENTIFIER)('b') + PsiWhiteSpace('\n ') + PsiElement(RBRACE)('}') + PsiWhiteSpace('\n\n ') + WHILE + PsiElement(while)('while') + PsiWhiteSpace(' ') + PsiElement(LPAR)('(') + CONDITION + BINARY_EXPRESSION + PsiElement(IDENTIFIER)('a') + PsiWhiteSpace(' ') + PsiElement(in)('in') + PsiWhiteSpace(' ') + PsiElement(IDENTIFIER)('b') + PsiElement(RPAR)(')') + BODY + + PsiElement(SEMICOLON)(';') + PsiWhiteSpace('\n ') + PsiElement(IDENTIFIER)('b') + PsiWhiteSpace('\n\n ') + WHILE + PsiElement(while)('while') + PsiWhiteSpace(' ') + PsiElement(LPAR)('(') + CONDITION + PsiElement(IDENTIFIER)('a') + PsiElement(RPAR)(')') + PsiWhiteSpace(' ') + BODY + FUNCTION_LITERAL + PsiElement(LBRACE)('{') + BODY + + PsiElement(RBRACE)('}') + PsiWhiteSpace('\n\n ') + IF + PsiElement(if)('if') + PsiWhiteSpace(' ') + PsiElement(LPAR)('(') + CONDITION + PsiElement(IDENTIFIER)('a') + PsiElement(RPAR)(')') + PsiWhiteSpace('\n ') + THEN + PsiElement(IDENTIFIER)('b') + PsiWhiteSpace('\n ') + PsiElement(else)('else') + PsiWhiteSpace('\n ') + ELSE + PsiElement(IDENTIFIER)('c') + PsiWhiteSpace('\n\n ') + IF + PsiElement(if)('if') + PsiWhiteSpace(' ') + PsiElement(LPAR)('(') + CONDITION + PsiElement(IDENTIFIER)('a') + PsiElement(RPAR)(')') + PsiWhiteSpace(' ') + THEN + PsiElement(IDENTIFIER)('b') + PsiWhiteSpace(' ') + PsiElement(else)('else') + PsiWhiteSpace(' ') + ELSE + PsiElement(IDENTIFIER)('c') + PsiWhiteSpace('\n\n ') + IF + PsiElement(if)('if') + PsiWhiteSpace(' ') + PsiElement(LPAR)('(') + CONDITION + PsiElement(IDENTIFIER)('a') + PsiElement(RPAR)(')') + PsiWhiteSpace(' ') + THEN + PsiElement(IDENTIFIER)('b') + PsiWhiteSpace('\n ') + PsiElement(else)('else') + PsiWhiteSpace(' ') + ELSE + PsiElement(IDENTIFIER)('c') + PsiWhiteSpace('\n\n ') + IF + PsiElement(if)('if') + PsiWhiteSpace(' ') + PsiElement(LPAR)('(') + CONDITION + PsiElement(IDENTIFIER)('a') + PsiElement(RPAR)(')') + PsiWhiteSpace('\n ') + THEN + PsiElement(IDENTIFIER)('b') + PsiElement(SEMICOLON)(';') + PsiWhiteSpace('\n ') + PsiElement(else)('else') + PsiWhiteSpace('\n ') + ELSE + PsiElement(IDENTIFIER)('c') + PsiElement(SEMICOLON)(';') + PsiWhiteSpace('\n\n ') + IF + PsiElement(if)('if') + PsiWhiteSpace(' ') + PsiElement(LPAR)('(') + CONDITION + PsiElement(IDENTIFIER)('a') + PsiElement(RPAR)(')') + PsiWhiteSpace(' ') + THEN + PsiElement(IDENTIFIER)('b') + PsiWhiteSpace('\n ') + IF + PsiElement(if)('if') + PsiWhiteSpace(' ') + PsiElement(LPAR)('(') + CONDITION + PsiElement(IDENTIFIER)('a') + PsiElement(RPAR)(')') + PsiWhiteSpace('\n ') + THEN + PsiElement(IDENTIFIER)('b') + PsiWhiteSpace('\n ') + IF + PsiElement(if)('if') + PsiWhiteSpace(' ') + PsiElement(LPAR)('(') + CONDITION + PsiElement(IDENTIFIER)('a') + PsiElement(RPAR)(')') + PsiWhiteSpace('\n ') + THEN + PsiElement(IDENTIFIER)('b') + PsiElement(SEMICOLON)(';') + PsiWhiteSpace('\n\n ') + IF + PsiElement(if)('if') + PsiWhiteSpace(' ') + PsiElement(LPAR)('(') + CONDITION + PsiElement(IDENTIFIER)('a') + PsiElement(RPAR)(')') + PsiWhiteSpace(' ') + THEN + + PsiElement(else)('else') + PsiWhiteSpace(' ') + ELSE + PsiElement(IDENTIFIER)('c') + PsiWhiteSpace('\n ') + IF + PsiElement(if)('if') + PsiWhiteSpace(' ') + PsiElement(LPAR)('(') + CONDITION + PsiElement(IDENTIFIER)('a') + PsiElement(RPAR)(')') + PsiWhiteSpace('\n ') + THEN + + PsiElement(else)('else') + PsiWhiteSpace(' ') + ELSE + PsiElement(IDENTIFIER)('c') + PsiWhiteSpace('\n ') + IF + PsiElement(if)('if') + PsiWhiteSpace(' ') + PsiElement(LPAR)('(') + CONDITION + PsiElement(IDENTIFIER)('a') + PsiElement(RPAR)(')') + PsiWhiteSpace('\n ') + THEN + PsiElement(SEMICOLON)(';') + PsiWhiteSpace('\n ') + PsiElement(else)('else') + PsiWhiteSpace(' ') + ELSE + PsiElement(IDENTIFIER)('c') + PsiWhiteSpace('\n ') + IF + PsiElement(if)('if') + PsiWhiteSpace(' ') + PsiElement(LPAR)('(') + CONDITION + PsiElement(IDENTIFIER)('a') + PsiElement(RPAR)(')') + PsiWhiteSpace('\n ') + THEN + + PsiElement(else)('else') + PsiWhiteSpace(' ') + ELSE + + PsiElement(SEMICOLON)(';') + PsiWhiteSpace('\n\n ') + DO_WHILE + PsiElement(do)('do') + PsiWhiteSpace(' ') + PsiElement(while)('while') + PsiWhiteSpace(' ') + PsiElement(LPAR)('(') + CONDITION + PsiElement(IDENTIFIER)('r') + PsiElement(RPAR)(')') + PsiWhiteSpace('\n ') + DO_WHILE + PsiElement(do)('do') + PsiWhiteSpace(' ') + BODY + PsiElement(IDENTIFIER)('foo') + PsiWhiteSpace(' ') + PsiElement(while)('while') + PsiWhiteSpace(' ') + PsiElement(LPAR)('(') + CONDITION + PsiElement(IDENTIFIER)('r') + PsiElement(RPAR)(')') + PsiWhiteSpace('\n ') + DO_WHILE + PsiElement(do)('do') + PsiWhiteSpace(' ') + BODY + FUNCTION_LITERAL + PsiElement(LBRACE)('{') + BODY + PsiElement(SEMICOLON)(';') + PsiElement(SEMICOLON)(';') + PsiElement(SEMICOLON)(';') + PsiElement(IDENTIFIER)('foo') + PsiElement(SEMICOLON)(';') + PsiElement(IDENTIFIER)('bar') + PsiElement(SEMICOLON)(';') + PsiElement(SEMICOLON)(';') + PsiElement(SEMICOLON)(';') + PsiElement(SEMICOLON)(';') + PsiWhiteSpace(' ') + PsiElement(RBRACE)('}') + PsiWhiteSpace(' ') + PsiElement(while)('while') + PsiWhiteSpace(' ') + PsiElement(LPAR)('(') + CONDITION + PsiElement(IDENTIFIER)('r') + PsiElement(RPAR)(')') + PsiWhiteSpace('\n') + PsiElement(RBRACE)('}') + PsiErrorElement:Expecting '} + \ No newline at end of file diff --git a/idea/testData/psi/EOLsOnRollback.jet b/idea/testData/psi/EOLsOnRollback.jet new file mode 100644 index 00000000000..63ed7804abd --- /dev/null +++ b/idea/testData/psi/EOLsOnRollback.jet @@ -0,0 +1,12 @@ +fun foo() { + class foo + fun foo() + extension foo for boo + + type x = t + decomposer (df) + + var r + + [a] var foo = 4 +} \ No newline at end of file diff --git a/idea/testData/psi/EOLsOnRollback.txt b/idea/testData/psi/EOLsOnRollback.txt new file mode 100644 index 00000000000..ded9cfd2ae0 --- /dev/null +++ b/idea/testData/psi/EOLsOnRollback.txt @@ -0,0 +1,90 @@ +JetFile: EOLsOnRollback.jet + NAMESPACE + FUN + PsiElement(fun)('fun') + PsiWhiteSpace(' ') + PsiElement(IDENTIFIER)('foo') + TYPE_PARAMETER_LIST + + VALUE_PARAMETER_LIST + PsiElement(LPAR)('(') + PsiElement(RPAR)(')') + PsiWhiteSpace(' ') + BLOCK + PsiElement(LBRACE)('{') + PsiWhiteSpace('\n ') + CLASS + PsiElement(class)('class') + PsiWhiteSpace(' ') + PsiElement(IDENTIFIER)('foo') + PsiWhiteSpace('\n ') + TYPE_PARAMETER_LIST + + FUN + PsiElement(fun)('fun') + PsiWhiteSpace(' ') + PsiElement(IDENTIFIER)('foo') + TYPE_PARAMETER_LIST + + VALUE_PARAMETER_LIST + PsiElement(LPAR)('(') + PsiElement(RPAR)(')') + PsiWhiteSpace('\n ') + EXTENSION + PsiElement(extension)('extension') + PsiWhiteSpace(' ') + PsiElement(IDENTIFIER)('foo') + PsiWhiteSpace(' ') + TYPE_PARAMETER_LIST + + PsiElement(for)('for') + PsiWhiteSpace(' ') + TYPE_REFERENCE + USER_TYPE + PsiElement(IDENTIFIER)('boo') + PsiWhiteSpace('\n\n ') + TYPEDEF + PsiElement(type)('type') + PsiWhiteSpace(' ') + PsiElement(IDENTIFIER)('x') + PsiWhiteSpace(' ') + TYPE_PARAMETER_LIST + + PsiElement(EQ)('=') + PsiWhiteSpace(' ') + TYPE_REFERENCE + USER_TYPE + PsiElement(IDENTIFIER)('t') + PsiWhiteSpace('\n ') + DECOMPOSER + PsiElement(decomposer)('decomposer') + PsiWhiteSpace(' ') + DECOMPOSER_PROPERTY_LIST + PsiElement(LPAR)('(') + PsiElement(IDENTIFIER)('df') + PsiElement(RPAR)(')') + PsiWhiteSpace('\n\n ') + PROPERTY + PsiElement(var)('var') + PsiWhiteSpace(' ') + PsiElement(IDENTIFIER)('r') + PsiWhiteSpace('\n\n ') + PROPERTY + MODIFIER_LIST + ATTRIBUTE_ANNOTATION + PsiElement(LBRACKET)('[') + ATTRIBUTE + USER_TYPE + PsiElement(IDENTIFIER)('a') + PsiElement(RBRACKET)(']') + PsiWhiteSpace(' ') + PsiElement(var)('var') + PsiWhiteSpace(' ') + PsiElement(IDENTIFIER)('foo') + PsiWhiteSpace(' ') + PsiElement(EQ)('=') + PsiWhiteSpace(' ') + INTEGER_CONSTANT + PsiElement(INTEGER_LITERAL)('4') + PsiWhiteSpace('\n') + PsiElement(RBRACE)('}') \ No newline at end of file diff --git a/idea/testData/psi/NewlinesInParentheses.jet b/idea/testData/psi/NewlinesInParentheses.jet new file mode 100644 index 00000000000..068dbb5d165 --- /dev/null +++ b/idea/testData/psi/NewlinesInParentheses.jet @@ -0,0 +1,12 @@ +fun foo() { + val a = a + b + val a = a + + b + val a = a + + b + val a = (a + + b) + + val a = b[c + + d] +} \ No newline at end of file diff --git a/idea/testData/psi/NewlinesInParentheses.txt b/idea/testData/psi/NewlinesInParentheses.txt new file mode 100644 index 00000000000..da0564c9a8f --- /dev/null +++ b/idea/testData/psi/NewlinesInParentheses.txt @@ -0,0 +1,94 @@ +JetFile: NewlinesInParentheses.jet + NAMESPACE + FUN + PsiElement(fun)('fun') + PsiWhiteSpace(' ') + PsiElement(IDENTIFIER)('foo') + TYPE_PARAMETER_LIST + + VALUE_PARAMETER_LIST + PsiElement(LPAR)('(') + PsiElement(RPAR)(')') + PsiWhiteSpace(' ') + BLOCK + PsiElement(LBRACE)('{') + PsiWhiteSpace('\n ') + PROPERTY + PsiElement(val)('val') + PsiWhiteSpace(' ') + PsiElement(IDENTIFIER)('a') + PsiWhiteSpace(' ') + PsiElement(EQ)('=') + PsiWhiteSpace(' ') + BINARY_EXPRESSION + PsiElement(IDENTIFIER)('a') + PsiWhiteSpace(' ') + PsiElement(PLUS)('+') + PsiWhiteSpace(' ') + PsiElement(IDENTIFIER)('b') + PsiWhiteSpace('\n ') + PROPERTY + PsiElement(val)('val') + PsiWhiteSpace(' ') + PsiElement(IDENTIFIER)('a') + PsiWhiteSpace(' ') + PsiElement(EQ)('=') + PsiWhiteSpace(' ') + BINARY_EXPRESSION + PsiElement(IDENTIFIER)('a') + PsiWhiteSpace(' ') + PsiElement(PLUS)('+') + PsiWhiteSpace('\n ') + PsiElement(IDENTIFIER)('b') + PsiWhiteSpace('\n ') + PROPERTY + PsiElement(val)('val') + PsiWhiteSpace(' ') + PsiElement(IDENTIFIER)('a') + PsiWhiteSpace(' ') + PsiElement(EQ)('=') + PsiWhiteSpace(' ') + PsiElement(IDENTIFIER)('a') + PsiWhiteSpace('\n ') + PREFIX_EXPRESSION + PsiElement(PLUS)('+') + PsiWhiteSpace(' ') + PsiElement(IDENTIFIER)('b') + PsiWhiteSpace('\n ') + PROPERTY + PsiElement(val)('val') + PsiWhiteSpace(' ') + PsiElement(IDENTIFIER)('a') + PsiWhiteSpace(' ') + PsiElement(EQ)('=') + PsiWhiteSpace(' ') + TUPLE + PsiElement(LPAR)('(') + BINARY_EXPRESSION + PsiElement(IDENTIFIER)('a') + PsiWhiteSpace('\n ') + PsiElement(PLUS)('+') + PsiWhiteSpace(' ') + PsiElement(IDENTIFIER)('b') + PsiElement(RPAR)(')') + PsiWhiteSpace('\n\n ') + PROPERTY + PsiElement(val)('val') + PsiWhiteSpace(' ') + PsiElement(IDENTIFIER)('a') + PsiWhiteSpace(' ') + PsiElement(EQ)('=') + PsiWhiteSpace(' ') + ARRAY_ACCESS_EXPRESSION + PsiElement(IDENTIFIER)('b') + INDICES + PsiElement(LBRACKET)('[') + BINARY_EXPRESSION + PsiElement(IDENTIFIER)('c') + PsiWhiteSpace('\n ') + PsiElement(PLUS)('+') + PsiWhiteSpace(' ') + PsiElement(IDENTIFIER)('d') + PsiElement(RBRACKET)(']') + PsiWhiteSpace('\n') + PsiElement(RBRACE)('}') \ No newline at end of file diff --git a/idea/testData/psi/SimpleClassMembers_ERR.jet b/idea/testData/psi/SimpleClassMembers_ERR.jet index c6688422601..258089e8257 100644 --- a/idea/testData/psi/SimpleClassMembers_ERR.jet +++ b/idea/testData/psi/SimpleClassMembers_ERR.jet @@ -12,7 +12,7 @@ class foo { var f - type foo = + type foo = ; decomposer (a, b, c @@ -26,4 +26,4 @@ class foo { } -} +} \ No newline at end of file diff --git a/idea/testData/psi/SimpleClassMembers_ERR.txt b/idea/testData/psi/SimpleClassMembers_ERR.txt index a492c8278f9..cf93599347d 100644 --- a/idea/testData/psi/SimpleClassMembers_ERR.txt +++ b/idea/testData/psi/SimpleClassMembers_ERR.txt @@ -74,10 +74,12 @@ JetFile: SimpleClassMembers_ERR.jet TYPE_PARAMETER_LIST PsiElement(EQ)('=') - PsiWhiteSpace('\n\n ') + PsiWhiteSpace(' ') TYPE_REFERENCE PsiErrorElement:Type expected + PsiElement(SEMICOLON)(';') + PsiWhiteSpace('\n\n ') DECOMPOSER PsiElement(decomposer)('decomposer') PsiWhiteSpace(' ') @@ -92,49 +94,50 @@ JetFile: SimpleClassMembers_ERR.jet PsiElement(IDENTIFIER)('c') PsiErrorElement:Expecting ',' or a closing ')' - PsiWhiteSpace('\n\n ') + PsiWhiteSpace('\n\n ') + PsiElement(this)('this') + PsiElement(LPAR)('(') + PsiElement(RPAR)(')') + PsiWhiteSpace(' ') + PsiErrorElement:Expecting member declaration + PsiElement(COLON)(':') + PsiWhiteSpace(' ') CONSTRUCTOR PsiElement(this)('this') VALUE_PARAMETER_LIST PsiElement(LPAR)('(') - PsiElement(RPAR)(')') - PsiWhiteSpace(' ') - PsiElement(COLON)(':') - PsiWhiteSpace(' ') - INITIALIZER_LIST - THIS_CALL - PsiElement(this)('this') - VALUE_ARGUMENT_LIST - PsiElement(LPAR)('(') - VALUE_ARGUMENT - PsiElement(IDENTIFIER)('a') - PsiElement(COMMA)(',') - PsiWhiteSpace(' ') - VALUE_ARGUMENT - PsiElement(IDENTIFIER)('b') - PsiElement(COMMA)(',') - PsiWhiteSpace(' ') - VALUE_ARGUMENT - PsiElement(IDENTIFIER)('c') - PsiElement(RPAR)(')') + VALUE_PARAMETER + PsiElement(IDENTIFIER)('a') + PsiErrorElement:Parameters must have type annotation + PsiElement(COMMA)(',') PsiWhiteSpace(' ') - DELEGATOR_SUPER_CALL - TYPE_REFERENCE - USER_TYPE - PsiElement(IDENTIFIER)('Foo') - TYPE_ARGUMENT_LIST - PsiElement(LT)('<') - TYPE_REFERENCE - USER_TYPE - PsiElement(IDENTIFIER)('T') - PsiErrorElement:Expecting a '>' - - VALUE_ARGUMENT_LIST - PsiElement(LPAR)('(') - VALUE_ARGUMENT - PsiElement(IDENTIFIER)('bar') - PsiElement(RPAR)(')') + VALUE_PARAMETER + PsiElement(IDENTIFIER)('b') + PsiErrorElement:Parameters must have type annotation + + PsiElement(COMMA)(',') + PsiWhiteSpace(' ') + VALUE_PARAMETER + PsiElement(IDENTIFIER)('c') + PsiErrorElement:Parameters must have type annotation + + PsiElement(RPAR)(')') + PsiErrorElement:Expecting member declaration + PsiElement(COMMA)(',') + PsiWhiteSpace(' ') + PsiErrorElement:Expecting member declaration + PsiElement(IDENTIFIER)('Foo') + PsiErrorElement:Expecting member declaration + PsiElement(LT)('<') + PsiErrorElement:Expecting member declaration + PsiElement(IDENTIFIER)('T') + PsiErrorElement:Expecting member declaration + PsiElement(LPAR)('(') + PsiErrorElement:Expecting member declaration + PsiElement(IDENTIFIER)('bar') + PsiErrorElement:Expecting member declaration + PsiElement(RPAR)(')') PsiWhiteSpace('\n\n ') CONSTRUCTOR PsiElement(this)('this') diff --git a/idea/tests/org/jetbrains/jet/parsing/JetParsingTest.java b/idea/tests/org/jetbrains/jet/parsing/JetParsingTest.java index 0047169dd6b..1055f6da5ee 100644 --- a/idea/tests/org/jetbrains/jet/parsing/JetParsingTest.java +++ b/idea/tests/org/jetbrains/jet/parsing/JetParsingTest.java @@ -38,6 +38,7 @@ public class JetParsingTest extends ParsingTestCase { public void testDecomposers_ERR() throws Exception {doTest(true);} public void testEmptyFile() throws Exception {doTest(true);} public void testEnums() throws Exception {doTest(true);} + public void testEOLsOnRollback() throws Exception {doTest(true);} public void testExtensions() throws Exception {doTest(true);} public void testExtensions_ERR() throws Exception {doTest(true);} public void testFileStart_ERR() throws Exception {doTest(true);} @@ -53,6 +54,7 @@ public class JetParsingTest extends ParsingTestCase { public void testLocalDeclarations() throws Exception {doTest(true);} public void testNamespaceBlock_ERR() throws Exception {doTest(true);} public void testNamespaceBlock() throws Exception {doTest(true);} + public void testNewlinesInParentheses() throws Exception {doTest(true);} public void testProperties() throws Exception {doTest(true);} public void testProperties_ERR() throws Exception {doTest(true);} public void testSimpleClassMembers() throws Exception {doTest(true);}