From 56efcd15aa5f78f4b89d91c1fb01ae6ea5b9d76a Mon Sep 17 00:00:00 2001 From: Nikolay Krasko Date: Fri, 20 Jun 2014 19:15:18 +0400 Subject: [PATCH] Smart enter for 'when' #KT-3600 In Progress --- .../jet/lang/psi/JetWhenExpression.java | 23 +++- .../plugin/editor/KotlinSmartEnterHandler.kt | 14 +-- .../fixers/KotlinMissingWhenBodyFixer.kt | 49 +++++++++ .../fixers/KotlinWhenSubjectCaretFixer.kt | 37 +++++++ .../plugin/quickfix/AddWhenElseBranchFix.java | 6 +- .../codeInsight/smartEnter/SmartEnterTest.kt | 103 ++++++++++++++++++ 6 files changed, 217 insertions(+), 15 deletions(-) create mode 100644 idea/src/org/jetbrains/jet/plugin/editor/fixers/KotlinMissingWhenBodyFixer.kt create mode 100644 idea/src/org/jetbrains/jet/plugin/editor/fixers/KotlinWhenSubjectCaretFixer.kt diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/psi/JetWhenExpression.java b/compiler/frontend/src/org/jetbrains/jet/lang/psi/JetWhenExpression.java index 59d6e08d1b4..74de6355425 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/psi/JetWhenExpression.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/psi/JetWhenExpression.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2013 JetBrains s.r.o. + * Copyright 2010-2014 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -47,13 +47,28 @@ public class JetWhenExpression extends JetExpressionImpl { @NotNull public PsiElement getWhenKeywordElement() { + //noinspection ConstantConditions return findChildByType(JetTokens.WHEN_KEYWORD); } @Nullable - public PsiElement getCloseBraceNode() { - ASTNode openBraceNode = getNode().findChildByType(JetTokens.RBRACE); - return openBraceNode != null ? openBraceNode.getPsi() : null; + public PsiElement getCloseBrace() { + return findChildByType(JetTokens.RBRACE); + } + + @Nullable + public PsiElement getOpenBrace() { + return findChildByType(JetTokens.LBRACE); + } + + @Nullable + public PsiElement getLeftParenthesis() { + return findChildByType(JetTokens.LPAR); + } + + @Nullable + public PsiElement getRightParenthesis() { + return findChildByType(JetTokens.RPAR); } @Nullable diff --git a/idea/src/org/jetbrains/jet/plugin/editor/KotlinSmartEnterHandler.kt b/idea/src/org/jetbrains/jet/plugin/editor/KotlinSmartEnterHandler.kt index f831ffc8050..e377dfde05d 100644 --- a/idea/src/org/jetbrains/jet/plugin/editor/KotlinSmartEnterHandler.kt +++ b/idea/src/org/jetbrains/jet/plugin/editor/KotlinSmartEnterHandler.kt @@ -16,9 +16,8 @@ package org.jetbrains.jet.plugin.editor +import org.jetbrains.jet.plugin.editor.fixers.* import com.intellij.lang.SmartEnterProcessorWithFixers -import org.jetbrains.jet.plugin.editor.fixers.KotlinIfConditionFixer -import org.jetbrains.jet.plugin.editor.fixers.KotlinMissingIfBranchFixer import com.intellij.openapi.editor.Editor import com.intellij.psi.PsiFile import com.intellij.util.text.CharArrayUtil @@ -31,11 +30,7 @@ import org.jetbrains.jet.lang.psi.JetBlockExpression import org.jetbrains.jet.lang.psi.JetExpression import org.jetbrains.jet.lang.psi.JetDeclarationWithBody import org.jetbrains.jet.lang.psi.JetIfExpression -import org.jetbrains.jet.plugin.editor.fixers.KotlinWhileConditionFixer import org.jetbrains.jet.lang.psi.JetWhileExpression -import org.jetbrains.jet.plugin.editor.fixers.isWithCaret -import org.jetbrains.jet.plugin.editor.fixers.KotlinForConditionFixer -import org.jetbrains.jet.plugin.editor.fixers.KotlinMissingForOrWhileBodyFixer import org.jetbrains.jet.lang.psi.JetForExpression import com.intellij.psi.tree.TokenSet import org.jetbrains.jet.JetNodeTypes @@ -48,7 +43,10 @@ public class KotlinSmartEnterHandler: SmartEnterProcessorWithFixers() { KotlinWhileConditionFixer(), KotlinForConditionFixer(), - KotlinMissingForOrWhileBodyFixer() + KotlinMissingForOrWhileBodyFixer(), + + KotlinWhenSubjectCaretFixer(), + KotlinMissingWhenBodyFixer() ) addEnterProcessors(KotlinPlainEnterProcessor()) @@ -145,4 +143,4 @@ public class KotlinSmartEnterHandler: SmartEnterProcessorWithFixers() { } } -private val IF_BRANCHES_CONTAINERS = TokenSet.create(JetNodeTypes.THEN, JetNodeTypes.ELSE) \ No newline at end of file +private val IF_BRANCHES_CONTAINERS = TokenSet.create(JetNodeTypes.THEN, JetNodeTypes.ELSE) diff --git a/idea/src/org/jetbrains/jet/plugin/editor/fixers/KotlinMissingWhenBodyFixer.kt b/idea/src/org/jetbrains/jet/plugin/editor/fixers/KotlinMissingWhenBodyFixer.kt new file mode 100644 index 00000000000..d53672fa617 --- /dev/null +++ b/idea/src/org/jetbrains/jet/plugin/editor/fixers/KotlinMissingWhenBodyFixer.kt @@ -0,0 +1,49 @@ +/* + * Copyright 2010-2014 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.jet.plugin.editor.fixers + +import com.intellij.lang.SmartEnterProcessorWithFixers +import org.jetbrains.jet.plugin.editor.KotlinSmartEnterHandler +import com.intellij.openapi.editor.Editor +import com.intellij.psi.PsiElement +import org.jetbrains.jet.lang.psi.JetWhenExpression + +public class KotlinMissingWhenBodyFixer : SmartEnterProcessorWithFixers.Fixer() { + override fun apply(editor: Editor, processor: KotlinSmartEnterHandler, element: PsiElement) { + if (element !is JetWhenExpression) return + val whenExpression = element as JetWhenExpression + + val doc = editor.getDocument() + + val openBrace = whenExpression.getOpenBrace() + val closeBrace = whenExpression.getCloseBrace() + + if (openBrace == null && closeBrace == null && whenExpression.getEntries().isEmpty()) { + val openBraceAfter = whenExpression.insertOpenBraceAfter() + if (openBraceAfter != null) { + doc.insertString(openBraceAfter.range.end, "{}") + } + } + } + + fun JetWhenExpression.insertOpenBraceAfter(): PsiElement? = when { + getRightParenthesis() != null -> getRightParenthesis() + getSubjectExpression() != null -> null + getLeftParenthesis() != null -> null + else -> getWhenKeywordElement() + } +} \ No newline at end of file diff --git a/idea/src/org/jetbrains/jet/plugin/editor/fixers/KotlinWhenSubjectCaretFixer.kt b/idea/src/org/jetbrains/jet/plugin/editor/fixers/KotlinWhenSubjectCaretFixer.kt new file mode 100644 index 00000000000..3da11c84eec --- /dev/null +++ b/idea/src/org/jetbrains/jet/plugin/editor/fixers/KotlinWhenSubjectCaretFixer.kt @@ -0,0 +1,37 @@ +/* + * Copyright 2010-2014 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.jet.plugin.editor.fixers + +import com.intellij.psi.PsiElement +import com.intellij.lang.SmartEnterProcessorWithFixers +import org.jetbrains.jet.plugin.editor.KotlinSmartEnterHandler +import com.intellij.openapi.editor.Editor +import org.jetbrains.jet.lang.psi.JetWhenExpression + +public class KotlinWhenSubjectCaretFixer : SmartEnterProcessorWithFixers.Fixer() { + override fun apply(editor: Editor, processor: KotlinSmartEnterHandler, element: PsiElement) { + if (element !is JetWhenExpression) return + + val lParen = element.getLeftParenthesis() + val rParen = element.getRightParenthesis() + val subject = element.getSubjectExpression() + + if (subject == null && lParen != null && rParen != null) { + processor.registerUnresolvedError(lParen.range.end) + } + } +} \ No newline at end of file diff --git a/idea/src/org/jetbrains/jet/plugin/quickfix/AddWhenElseBranchFix.java b/idea/src/org/jetbrains/jet/plugin/quickfix/AddWhenElseBranchFix.java index bd43ce97664..276d3b51041 100644 --- a/idea/src/org/jetbrains/jet/plugin/quickfix/AddWhenElseBranchFix.java +++ b/idea/src/org/jetbrains/jet/plugin/quickfix/AddWhenElseBranchFix.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2013 JetBrains s.r.o. + * Copyright 2010-2014 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -55,12 +55,12 @@ public class AddWhenElseBranchFix extends JetIntentionAction @Override public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) { - return super.isAvailable(project, editor, file) && element.getCloseBraceNode() != null; + return super.isAvailable(project, editor, file) && element.getCloseBrace() != null; } @Override public void invoke(@NotNull Project project, Editor editor, JetFile file) throws IncorrectOperationException { - PsiElement whenCloseBrace = element.getCloseBraceNode(); + PsiElement whenCloseBrace = element.getCloseBrace(); assert (whenCloseBrace != null) : "isAvailable should check if close brace exist"; JetWhenEntry entry = JetPsiFactory.createWhenEntry(project, ELSE_ENTRY_TEXT); diff --git a/idea/tests/org/jetbrains/jet/plugin/codeInsight/smartEnter/SmartEnterTest.kt b/idea/tests/org/jetbrains/jet/plugin/codeInsight/smartEnter/SmartEnterTest.kt index de8e7de579f..cc64f47fe31 100644 --- a/idea/tests/org/jetbrains/jet/plugin/codeInsight/smartEnter/SmartEnterTest.kt +++ b/idea/tests/org/jetbrains/jet/plugin/codeInsight/smartEnter/SmartEnterTest.kt @@ -457,6 +457,109 @@ class SmartEnterTest : JetLightCodeInsightFixtureTestCase() { """ ) + + + fun testWhen() = doFunTest( + """ + when + """ + , + """ + when { + + } + """ + ) + + fun testWhen1() = doFunTest( + """ + when + """ + , + """ + when { + + } + """ + ) + + fun testWhen2() = doFunTest( + """ + when (true) { + } + """ + , + """ + when (true) { + + } + """ + ) + + fun testWhen3() = doFunTest( + """ + when (true) { + """ + , + """ + when (true) { + + } + """ + ) + + fun testWhen4() = doFunTest( + """ + when (true) { + false -> println("false") + } + """ + , + """ + when (true) { + + false -> println("false") + } + """ + ) + + fun testWhen5() = doFunTest( + """ + when () + """ + , + """ + when () { + } + """ + ) + + fun testWhen6() = doFunTest( + """ + when (true) + """ + , + """ + when (true) { + + } + """ + ) + + // Check that no addition {} inserted + fun testWhenBadParsed() = doFunTest( + """ + when ( { + } + """ + , + """ + when ( { + } + + """ + ) + fun doFunTest(before: String, after: String) { fun String.withFunContext(): String { val bodyText = "//----${this.trimIndent()}//----"