From f18b9cceb3f48bbbe5d36876a41bcd59ebb63071 Mon Sep 17 00:00:00 2001 From: Denis Zharkov Date: Mon, 11 Jul 2016 11:37:33 +0300 Subject: [PATCH] Implement 'not' postfix template #KT-4710 In Progress --- .../kotlin/generators/tests/GenerateTests.kt | 9 +- .../KtNotPostfixTemplate/after.kt.template | 5 + .../KtNotPostfixTemplate/before.kt.template | 5 + .../KtNotPostfixTemplate/description.html | 5 + idea/src/META-INF/plugin.xml | 5 + .../postfix/KtPostfixTemplateProvider.kt | 94 +++++++++++++++++++ .../codeInsight/postfix/notBoolean.kt | 3 + .../codeInsight/postfix/notBoolean.kt.after | 3 + .../testData/codeInsight/postfix/notString.kt | 3 + .../codeInsight/postfix/notString.kt.after | 3 + .../AbstractPostfixTemplateProviderTest.kt | 34 +++++++ .../PostfixTemplateProviderTestGenerated.java | 49 ++++++++++ 12 files changed, 216 insertions(+), 2 deletions(-) create mode 100644 idea/resources/postfixTemplates/KtNotPostfixTemplate/after.kt.template create mode 100644 idea/resources/postfixTemplates/KtNotPostfixTemplate/before.kt.template create mode 100644 idea/resources/postfixTemplates/KtNotPostfixTemplate/description.html create mode 100644 idea/src/org/jetbrains/kotlin/idea/codeInsight/postfix/KtPostfixTemplateProvider.kt create mode 100644 idea/testData/codeInsight/postfix/notBoolean.kt create mode 100644 idea/testData/codeInsight/postfix/notBoolean.kt.after create mode 100644 idea/testData/codeInsight/postfix/notString.kt create mode 100644 idea/testData/codeInsight/postfix/notString.kt.after create mode 100644 idea/tests/org/jetbrains/kotlin/idea/codeInsight/postfix/AbstractPostfixTemplateProviderTest.kt create mode 100644 idea/tests/org/jetbrains/kotlin/idea/codeInsight/postfix/PostfixTemplateProviderTestGenerated.java diff --git a/generators/src/org/jetbrains/kotlin/generators/tests/GenerateTests.kt b/generators/src/org/jetbrains/kotlin/generators/tests/GenerateTests.kt index 36543f895c6..5366120cffb 100755 --- a/generators/src/org/jetbrains/kotlin/generators/tests/GenerateTests.kt +++ b/generators/src/org/jetbrains/kotlin/generators/tests/GenerateTests.kt @@ -54,6 +54,7 @@ import org.jetbrains.kotlin.idea.codeInsight.generate.AbstractGenerateTestSuppor import org.jetbrains.kotlin.idea.codeInsight.generate.AbstractGenerateToStringActionTest import org.jetbrains.kotlin.idea.codeInsight.moveUpDown.AbstractMoveLeftRightTest import org.jetbrains.kotlin.idea.codeInsight.moveUpDown.AbstractMoveStatementTest +import org.jetbrains.kotlin.idea.codeInsight.postfix.AbstractPostfixTemplateProviderTest import org.jetbrains.kotlin.idea.codeInsight.surroundWith.AbstractSurroundWithTest import org.jetbrains.kotlin.idea.codeInsight.unwrap.AbstractUnwrapRemoveTest import org.jetbrains.kotlin.idea.completion.test.* @@ -852,6 +853,10 @@ fun main(args: Array) { model("repl/completion") } + testClass { + model("codeInsight/postfix") + } + testClass { model("script/definition/highlighting", extension = null, recursive = false) } @@ -1069,12 +1074,12 @@ fun main(args: Array) { model("collectToFile", recursive = false, extension = null) } } - + testGroup("plugins/plugins-tests/tests", "plugins/annotation-processing/testData") { testClass() { model("javaWrappers", extension = null) } - + testClass() { model("kotlinWrappers", extension = "kt") } diff --git a/idea/resources/postfixTemplates/KtNotPostfixTemplate/after.kt.template b/idea/resources/postfixTemplates/KtNotPostfixTemplate/after.kt.template new file mode 100644 index 00000000000..e32ca5fcbe3 --- /dev/null +++ b/idea/resources/postfixTemplates/KtNotPostfixTemplate/after.kt.template @@ -0,0 +1,5 @@ +fun foo(x: Boolean) { + if (!x) { + + } +} diff --git a/idea/resources/postfixTemplates/KtNotPostfixTemplate/before.kt.template b/idea/resources/postfixTemplates/KtNotPostfixTemplate/before.kt.template new file mode 100644 index 00000000000..5cd82bf4c6d --- /dev/null +++ b/idea/resources/postfixTemplates/KtNotPostfixTemplate/before.kt.template @@ -0,0 +1,5 @@ +fun foo(x: Boolean) { + if (x$key) { + + } +} diff --git a/idea/resources/postfixTemplates/KtNotPostfixTemplate/description.html b/idea/resources/postfixTemplates/KtNotPostfixTemplate/description.html new file mode 100644 index 00000000000..a66fddac6e4 --- /dev/null +++ b/idea/resources/postfixTemplates/KtNotPostfixTemplate/description.html @@ -0,0 +1,5 @@ + + +Negate the expression. + + diff --git a/idea/src/META-INF/plugin.xml b/idea/src/META-INF/plugin.xml index 7d7246d7652..bb71459324c 100644 --- a/idea/src/META-INF/plugin.xml +++ b/idea/src/META-INF/plugin.xml @@ -397,6 +397,11 @@ + + + + (KtNotPostfixTemplate) + + override fun isTerminalSymbol(currentChar: Char) = currentChar == '.' || currentChar == '!' + + override fun afterExpand(file: PsiFile, editor: Editor) { + } + + override fun preCheck(copyFile: PsiFile, realEditor: Editor, currentOffset: Int) = copyFile + + override fun preExpand(file: PsiFile, editor: Editor) { + } +} + +private object KtNotPostfixTemplate : NotPostfixTemplate( + KtPostfixTemplatePsiInfo, + createExpressionSelector { it.isBoolean() } +) + +private object KtPostfixTemplatePsiInfo : PostfixTemplatePsiInfo() { + override fun createExpression(context: PsiElement, prefix: String, suffix: String) = + KtPsiFactory(context.project).createExpression(prefix + context.text + suffix) + + override fun getNegatedExpression(element: PsiElement) = (element as KtExpression).negate() +} + +internal fun createExpressionSelector( + statementsOnly: Boolean = false, + predicate: ((KotlinType) -> Boolean)? = null +): PostfixTemplateExpressionSelectorBase = + KtExpressionPostfixTemplateSelector { + if (this !is KtExpression) return@KtExpressionPostfixTemplateSelector false + if (statementsOnly && !isStatement()) return@KtExpressionPostfixTemplateSelector false + + if (predicate == null) return@KtExpressionPostfixTemplateSelector true + + getType(analyze(BodyResolveMode.PARTIAL_FOR_COMPLETION))?.let { predicate(it) } ?: false + } + +private fun PsiElement.isStatement() = parent is KtBlockExpression + +private class KtExpressionPostfixTemplateSelector( + val filter: PsiElement.() -> Boolean +) : PostfixTemplateExpressionSelectorBase(Condition(filter)) { + override fun getNonFilteredExpressions( + context: PsiElement, + document: Document, + offset: Int + ) = context.parentsWithSelf + .filterIsInstance() + .takeWhile { it !is KtBlockExpression && it !is KtDeclarationWithBody && !it.isEffectivelyDeclaration() } + .toList() +} + +private fun KtElement.isEffectivelyDeclaration() = + this is KtNamedDeclaration && + // function literal is an expression while it's also a subtype of KtNamedDeclaration + this !is KtFunctionLiteral && + // !(fun (a) = 1) + (this !is KtNamedFunction || this.name == null) diff --git a/idea/testData/codeInsight/postfix/notBoolean.kt b/idea/testData/codeInsight/postfix/notBoolean.kt new file mode 100644 index 00000000000..6b7bdd23ecf --- /dev/null +++ b/idea/testData/codeInsight/postfix/notBoolean.kt @@ -0,0 +1,3 @@ +fun foo(x: Boolean) { + x.not +} diff --git a/idea/testData/codeInsight/postfix/notBoolean.kt.after b/idea/testData/codeInsight/postfix/notBoolean.kt.after new file mode 100644 index 00000000000..7918515dd2e --- /dev/null +++ b/idea/testData/codeInsight/postfix/notBoolean.kt.after @@ -0,0 +1,3 @@ +fun foo(x: Boolean) { + !x +} diff --git a/idea/testData/codeInsight/postfix/notString.kt b/idea/testData/codeInsight/postfix/notString.kt new file mode 100644 index 00000000000..065d2d8093d --- /dev/null +++ b/idea/testData/codeInsight/postfix/notString.kt @@ -0,0 +1,3 @@ +fun foo(x: String) { + x.not +} diff --git a/idea/testData/codeInsight/postfix/notString.kt.after b/idea/testData/codeInsight/postfix/notString.kt.after new file mode 100644 index 00000000000..065d3d60ea1 --- /dev/null +++ b/idea/testData/codeInsight/postfix/notString.kt.after @@ -0,0 +1,3 @@ +fun foo(x: String) { + x.not +} diff --git a/idea/tests/org/jetbrains/kotlin/idea/codeInsight/postfix/AbstractPostfixTemplateProviderTest.kt b/idea/tests/org/jetbrains/kotlin/idea/codeInsight/postfix/AbstractPostfixTemplateProviderTest.kt new file mode 100644 index 00000000000..826ba4e6f5d --- /dev/null +++ b/idea/tests/org/jetbrains/kotlin/idea/codeInsight/postfix/AbstractPostfixTemplateProviderTest.kt @@ -0,0 +1,34 @@ +/* + * Copyright 2010-2016 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.kotlin.idea.codeInsight.postfix + +import org.jetbrains.kotlin.idea.test.KotlinLightCodeInsightFixtureTestCase +import org.jetbrains.kotlin.idea.test.KotlinWithJdkAndRuntimeLightProjectDescriptor +import org.jetbrains.kotlin.test.KotlinTestUtils + + +abstract class AbstractPostfixTemplateProviderTest : KotlinLightCodeInsightFixtureTestCase() { + + override fun getProjectDescriptor() = KotlinWithJdkAndRuntimeLightProjectDescriptor.INSTANCE + override fun getTestDataPath() = KotlinTestUtils.getHomeDirectory() + + protected fun doTest(fileName: String) { + myFixture.configureByFile(fileName) + myFixture.type("\t") + myFixture.checkResultByFile(fileName + ".after") + } +} diff --git a/idea/tests/org/jetbrains/kotlin/idea/codeInsight/postfix/PostfixTemplateProviderTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/codeInsight/postfix/PostfixTemplateProviderTestGenerated.java new file mode 100644 index 00000000000..b8516781991 --- /dev/null +++ b/idea/tests/org/jetbrains/kotlin/idea/codeInsight/postfix/PostfixTemplateProviderTestGenerated.java @@ -0,0 +1,49 @@ +/* + * Copyright 2010-2016 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.kotlin.idea.codeInsight.postfix; + +import com.intellij.testFramework.TestDataPath; +import org.jetbrains.kotlin.test.JUnit3RunnerWithInners; +import org.jetbrains.kotlin.test.KotlinTestUtils; +import org.jetbrains.kotlin.test.TestMetadata; +import org.junit.runner.RunWith; + +import java.io.File; +import java.util.regex.Pattern; + +/** This class is generated by {@link org.jetbrains.kotlin.generators.tests.TestsPackage}. DO NOT MODIFY MANUALLY */ +@SuppressWarnings("all") +@TestMetadata("idea/testData/codeInsight/postfix") +@TestDataPath("$PROJECT_ROOT") +@RunWith(JUnit3RunnerWithInners.class) +public class PostfixTemplateProviderTestGenerated extends AbstractPostfixTemplateProviderTest { + public void testAllFilesPresentInPostfix() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/codeInsight/postfix"), Pattern.compile("^(.+)\\.kt$"), true); + } + + @TestMetadata("notBoolean.kt") + public void testNotBoolean() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/codeInsight/postfix/notBoolean.kt"); + doTest(fileName); + } + + @TestMetadata("notString.kt") + public void testNotString() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/codeInsight/postfix/notString.kt"); + doTest(fileName); + } +}