diff --git a/compiler/frontend/src/org/jetbrains/kotlin/KtNodeTypes.java b/compiler/frontend/src/org/jetbrains/kotlin/KtNodeTypes.java index f33eadff4a4..ecd3ea60595 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/KtNodeTypes.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/KtNodeTypes.java @@ -16,9 +16,20 @@ package org.jetbrains.kotlin; +import com.intellij.lang.ASTNode; +import com.intellij.lang.Language; +import com.intellij.lang.PsiBuilder; +import com.intellij.lang.PsiBuilderFactory; +import com.intellij.lexer.Lexer; +import com.intellij.openapi.project.Project; import com.intellij.psi.tree.IElementType; +import com.intellij.psi.tree.IErrorCounterReparseableElementType; import com.intellij.psi.tree.IFileElementType; +import org.jetbrains.annotations.Nullable; import org.jetbrains.kotlin.idea.KotlinLanguage; +import org.jetbrains.kotlin.lexer.KotlinLexer; +import org.jetbrains.kotlin.lexer.KtTokens; +import org.jetbrains.kotlin.parsing.KotlinParser; import org.jetbrains.kotlin.psi.*; import org.jetbrains.kotlin.psi.stubs.elements.KtStubElementTypes; @@ -115,7 +126,48 @@ public interface KtNodeTypes { KtNodeType LOOP_RANGE = new KtNodeType("LOOP_RANGE", KtContainerNode.class); KtNodeType BODY = new KtNodeType("BODY", KtContainerNode.class); KtNodeType BLOCK = new KtNodeType("BLOCK", KtBlockExpression.class); - KtNodeType LAMBDA_EXPRESSION = new KtNodeType("LAMBDA_EXPRESSION", KtLambdaExpression.class); + + IElementType LAMBDA_EXPRESSION = new IErrorCounterReparseableElementType("LAMBDA_EXPRESSION", KotlinLanguage.INSTANCE) { + @Override + public ASTNode parseContents(ASTNode chameleon) { + Project project = chameleon.getPsi().getProject(); + PsiBuilder builder = PsiBuilderFactory.getInstance().createBuilder(project, chameleon, null, KotlinLanguage.INSTANCE, chameleon.getChars()); + return KotlinParser.parseLambdaExpression(builder).getFirstChildNode(); + } + + @Nullable + @Override + public ASTNode createNode(CharSequence text) { + return new KtLambdaExpression(text); + } + + @Override + public int getErrorsCount(CharSequence seq, Language fileLanguage, Project project) { + Lexer lexer = new KotlinLexer(); + + lexer.start(seq); + if (lexer.getTokenType() != KtTokens.LBRACE) return IErrorCounterReparseableElementType.FATAL_ERROR; + lexer.advance(); + int balance = 1; + while (true) { + IElementType type = lexer.getTokenType(); + if (type == null) break; + if (balance == 0) { + return IErrorCounterReparseableElementType.FATAL_ERROR; + } + if (type == KtTokens.LBRACE) { + balance++; + } + else if (type == KtTokens.RBRACE) { + balance--; + } + lexer.advance(); + } + return balance; + } + }; + + KtNodeType FUNCTION_LITERAL = new KtNodeType("FUNCTION_LITERAL", KtFunctionLiteral.class); KtNodeType ANNOTATED_EXPRESSION = new KtNodeType("ANNOTATED_EXPRESSION", KtAnnotatedExpression.class); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/parsing/KotlinExpressionParsing.java b/compiler/frontend/src/org/jetbrains/kotlin/parsing/KotlinExpressionParsing.java index dd4af0d7c98..dd45361f1e5 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/parsing/KotlinExpressionParsing.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/parsing/KotlinExpressionParsing.java @@ -573,7 +573,7 @@ public class KotlinExpressionParsing extends AbstractKotlinParsing { return false; } - parseFunctionLiteral(preferBlock); + parseFunctionLiteral(preferBlock, /* collapse = */true); doneOrDrop(labeled, LABELED_EXPRESSION, wasLabel); doneOrDrop(annotated, ANNOTATED_EXPRESSION, wereAnnotations); @@ -1033,10 +1033,10 @@ public class KotlinExpressionParsing extends AbstractKotlinParsing { * ; */ private void parseFunctionLiteral() { - parseFunctionLiteral(/* preferBlock = */false); + parseFunctionLiteral(/* preferBlock = */false, /* collapse = */true); } - private void parseFunctionLiteral(boolean preferBlock) { + public void parseFunctionLiteral(boolean preferBlock, boolean collapse) { assert _at(LBRACE); PsiBuilder.Marker literalExpression = mark(); @@ -1088,7 +1088,12 @@ public class KotlinExpressionParsing extends AbstractKotlinParsing { myBuilder.restoreNewlinesState(); literal.done(FUNCTION_LITERAL); - literalExpression.done(LAMBDA_EXPRESSION); + if (collapse) { + literalExpression.collapse(LAMBDA_EXPRESSION); + } + else { + literalExpression.done(LAMBDA_EXPRESSION); + } } private boolean rollbackOrDropAt(PsiBuilder.Marker rollbackMarker, IElementType dropAt) { diff --git a/compiler/frontend/src/org/jetbrains/kotlin/parsing/KotlinParser.java b/compiler/frontend/src/org/jetbrains/kotlin/parsing/KotlinParser.java index 06b0483c522..979ac1ed8f0 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/parsing/KotlinParser.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/parsing/KotlinParser.java @@ -73,4 +73,11 @@ public class KotlinParser implements PsiParser { ktParsing.parseBlockCodeFragment(); return psiBuilder.getTreeBuilt(); } + + @NotNull + public static ASTNode parseLambdaExpression(PsiBuilder psiBuilder) { + KotlinParsing ktParsing = KotlinParsing.createForTopLevel(new SemanticWhitespaceAwarePsiBuilderImpl(psiBuilder)); + ktParsing.parseLambdaExpression(); + return psiBuilder.getTreeBuilt(); + } } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/parsing/KotlinParsing.java b/compiler/frontend/src/org/jetbrains/kotlin/parsing/KotlinParsing.java index 9abbb3d7fdb..cdc561bc844 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/parsing/KotlinParsing.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/parsing/KotlinParsing.java @@ -156,6 +156,10 @@ public class KotlinParsing extends AbstractKotlinParsing { marker.done(BLOCK_CODE_FRAGMENT); } + void parseLambdaExpression() { + myExpressionParsing.parseFunctionLiteral(/* preferBlock = */ false, /* collapse = */false); + } + void parseScript() { PsiBuilder.Marker fileMarker = mark(); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/psi/KtLambdaExpression.java b/compiler/frontend/src/org/jetbrains/kotlin/psi/KtLambdaExpression.java index f8781768371..67cf76249e7 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/psi/KtLambdaExpression.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/psi/KtLambdaExpression.java @@ -17,6 +17,9 @@ package org.jetbrains.kotlin.psi; import com.intellij.lang.ASTNode; +import com.intellij.psi.PsiElementVisitor; +import com.intellij.psi.PsiFile; +import com.intellij.psi.impl.source.tree.LazyParseablePsiElement; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.kotlin.KtNodeTypes; @@ -24,9 +27,9 @@ import org.jetbrains.kotlin.lexer.KtTokens; import java.util.List; -public class KtLambdaExpression extends KtExpressionImpl { - public KtLambdaExpression(@NotNull ASTNode node) { - super(node); +public class KtLambdaExpression extends LazyParseablePsiElement implements KtExpression { + public KtLambdaExpression(CharSequence text) { + super(KtNodeTypes.LAMBDA_EXPRESSION, text); } @Override @@ -36,7 +39,7 @@ public class KtLambdaExpression extends KtExpressionImpl { @NotNull public KtFunctionLiteral getFunctionLiteral() { - return (KtFunctionLiteral) findChildByType(KtNodeTypes.FUNCTION_LITERAL); + return findChildByType(KtNodeTypes.FUNCTION_LITERAL).getPsi(KtFunctionLiteral.class); } @NotNull @@ -67,4 +70,32 @@ public class KtLambdaExpression extends KtExpressionImpl { public ASTNode getRightCurlyBrace() { return getFunctionLiteral().getNode().findChildByType(KtTokens.RBRACE); } + + @NotNull + @Override + public KtFile getContainingKtFile() { + PsiFile file = getContainingFile(); + assert file instanceof KtFile : "KtElement not inside KtFile: " + file + " " + file.getText(); + return (KtFile) file; + } + + @Override + public void acceptChildren(@NotNull KtVisitor visitor, D data) { + KtPsiUtil.visitChildren(this, visitor, data); + } + + @Override + public final void accept(@NotNull PsiElementVisitor visitor) { + if (visitor instanceof KtVisitor) { + accept((KtVisitor) visitor, null); + } + else { + visitor.visitElement(this); + } + } + + @Override + public String toString() { + return getNode().getElementType().toString(); + } }