Introduce 'maino' and 'psvmo' templates for generating main in object (KT-6520)

#KT-6520 Fixed
(cherry picked from commit cf1bdba)
This commit is contained in:
Nikolay Krasko
2016-08-17 13:32:53 +03:00
committed by Nikolay Krasko
parent 48d7e8c519
commit 4d6206a7b5
11 changed files with 144 additions and 60 deletions
+1
View File
@@ -171,6 +171,7 @@ These artifacts include extensions for the types available in the latter JDKs, s
- [`KT-12997`](https://youtrack.jetbrains.com/issue/KT-12997) Override/Implement Members: Support "Copy JavaDoc" options for library classes
- [`KT-13207`](https://youtrack.jetbrains.com/issue/KT-13207) Exception on safe delete from intention in 2016.2
- [`KT-12887`](https://youtrack.jetbrains.com/issue/KT-12887) "Extend selection" should select call's invoked expression
- [`KT-6520`](https://youtrack.jetbrains.com/issue/KT-6520) Introduce 'maino' and 'psvmo' templates for generating main in object
- Fix exception when choose Gradle configurer after project is synced
- Allow configuring Kotlin in Gradle module without Kotlin sources
- Show all Kotlin annotations when browsing hierarchy of "java.lang.Annotation"
@@ -26,7 +26,7 @@ import com.intellij.psi.PsiWhiteSpace;
import com.intellij.psi.impl.source.tree.LeafPsiElement;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtilBase;
import com.intellij.psi.util.PsiUtilCore;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -35,44 +35,50 @@ import org.jetbrains.kotlin.lexer.KtTokens;
import org.jetbrains.kotlin.psi.*;
public abstract class KotlinTemplateContextType extends TemplateContextType {
protected KotlinTemplateContextType(@NotNull @NonNls String id, @NotNull String presentableName, @Nullable java.lang.Class<? extends TemplateContextType> baseContextType) {
private KotlinTemplateContextType(
@NotNull @NonNls String id,
@NotNull String presentableName,
@Nullable java.lang.Class<? extends TemplateContextType> baseContextType
) {
super(id, presentableName, baseContextType);
}
@Override
public boolean isInContext(@NotNull PsiFile file, int offset) {
if (PsiUtilBase.getLanguageAtOffset(file, offset).isKindOf(KotlinLanguage.INSTANCE)) {
PsiElement element = file.findElementAt(offset);
if (element == null) {
element = file.findElementAt(offset - 1);
}
if (element instanceof PsiWhiteSpace) {
return false;
}
else if (PsiTreeUtil.getParentOfType(element, PsiComment.class, false) != null) {
return isCommentInContext();
}
else if (PsiTreeUtil.getParentOfType(element, KtPackageDirective.class) != null
|| PsiTreeUtil.getParentOfType(element, KtImportDirective.class) != null) {
return false;
}
else if (element instanceof LeafPsiElement) {
IElementType elementType = ((LeafPsiElement) element).getElementType();
if (elementType == KtTokens.IDENTIFIER) {
PsiElement parent = element.getParent();
if (parent instanceof KtReferenceExpression) {
PsiElement parentOfParent = parent.getParent();
KtQualifiedExpression qualifiedExpression = PsiTreeUtil.getParentOfType(element, KtQualifiedExpression.class);
if (qualifiedExpression != null && qualifiedExpression.getSelectorExpression() == parentOfParent) {
return false;
}
if (!PsiUtilCore.getLanguageAtOffset(file, offset).isKindOf(KotlinLanguage.INSTANCE)) {
return false;
}
PsiElement element = file.findElementAt(offset);
if (element == null) {
element = file.findElementAt(offset - 1);
}
if (element instanceof PsiWhiteSpace) {
return false;
}
else if (PsiTreeUtil.getParentOfType(element, PsiComment.class, false) != null) {
return isCommentInContext();
}
else if (PsiTreeUtil.getParentOfType(element, KtPackageDirective.class) != null
|| PsiTreeUtil.getParentOfType(element, KtImportDirective.class) != null) {
return false;
}
else if (element instanceof LeafPsiElement) {
IElementType elementType = ((LeafPsiElement) element).getElementType();
if (elementType == KtTokens.IDENTIFIER) {
PsiElement parent = element.getParent();
if (parent instanceof KtReferenceExpression) {
PsiElement parentOfParent = parent.getParent();
KtQualifiedExpression qualifiedExpression = PsiTreeUtil.getParentOfType(element, KtQualifiedExpression.class);
if (qualifiedExpression != null && qualifiedExpression.getSelectorExpression() == parentOfParent) {
return false;
}
}
}
return element != null && isInContext(element);
}
return false;
return element != null && isInContext(element);
}
protected boolean isCommentInContext() {
@@ -114,8 +120,7 @@ public abstract class KotlinTemplateContextType extends TemplateContextType {
}
continue;
}
if (e instanceof KtProperty || e instanceof KtNamedFunction
|| e instanceof KtClassOrObject) {
if (e instanceof KtProperty || e instanceof KtNamedFunction || e instanceof KtClassOrObject) {
return false;
}
if (e instanceof KtScriptInitializer) {
@@ -127,6 +132,18 @@ public abstract class KotlinTemplateContextType extends TemplateContextType {
}
}
public static class ObjectDeclaration extends KotlinTemplateContextType {
public ObjectDeclaration() {
super("KOTLIN_OBJECT_DECLARATION", "Object declaration", Generic.class);
}
@Override
protected boolean isInContext(@NotNull PsiElement element) {
KtObjectDeclaration objectDeclaration = getParentClassOrObject(element, KtObjectDeclaration.class);
return objectDeclaration != null && !objectDeclaration.isObjectLiteral();
}
}
public static class Class extends KotlinTemplateContextType {
public Class() {
super("KOTLIN_CLASS", "Class", Generic.class);
@@ -134,22 +151,7 @@ public abstract class KotlinTemplateContextType extends TemplateContextType {
@Override
protected boolean isInContext(@NotNull PsiElement element) {
PsiElement e = element;
while (e != null && !(e instanceof KtClassOrObject)) {
if (e instanceof KtModifierList) {
// skip property/function/class or object which is owner of modifier list
e = e.getParent();
if (e != null) {
e = e.getParent();
}
continue;
}
if (e instanceof KtProperty || e instanceof KtNamedFunction) {
return false;
}
e = e.getParent();
}
return e != null;
return getParentClassOrObject(element, KtClassOrObject.class) != null;
}
}
@@ -202,4 +204,25 @@ public abstract class KotlinTemplateContextType extends TemplateContextType {
return true;
}
}
private static <T extends PsiElement> T getParentClassOrObject(@NotNull PsiElement element, @NotNull java.lang.Class<? extends T> klass) {
PsiElement e = element;
while (e != null && !klass.isInstance(e)) {
if (e instanceof KtModifierList) {
// skip property/function/class or object which is owner of modifier list
e = e.getParent();
if (e != null) {
e = e.getParent();
}
continue;
}
if (e instanceof KtProperty || e instanceof KtNamedFunction) {
return null;
}
e = e.getParent();
}
//noinspection unchecked
return (T) e;
}
}
@@ -0,0 +1,3 @@
val test = object {
t<caret>t
}
@@ -0,0 +1,5 @@
class Some {
companion object {
t<caret>t
}
}
@@ -0,0 +1,5 @@
fun test() {
object Test {
t<caret>t
}
}
@@ -0,0 +1,5 @@
class Some {
object Test {
t<caret>t
}
}
@@ -0,0 +1,5 @@
object Some {
object Test {
t<caret>t
}
}
@@ -0,0 +1,3 @@
object Some {
t<caret>t
}
@@ -18,6 +18,7 @@ package org.jetbrains.kotlin.idea.liveTemplates
import com.intellij.codeInsight.template.TemplateContextType
import com.intellij.testFramework.UsefulTestCase
import org.jetbrains.kotlin.idea.liveTemplates.KotlinTemplateContextType.*
import org.jetbrains.kotlin.idea.test.KotlinLightCodeInsightFixtureTestCase
import java.io.File
@@ -26,27 +27,43 @@ class LiveTemplatesContextTest : KotlinLightCodeInsightFixtureTestCase() {
File(TEST_DATA_BASE_PATH, "/context").path + File.separator
fun testInDocComment() {
myFixture.configureByFile(getTestName(false) + ".kt")
assertInContexts(
KotlinTemplateContextType.Generic::class.java,
KotlinTemplateContextType.Comment::class.java)
assertInContexts(Generic::class.java, Comment::class.java)
}
fun testTopLevel() {
myFixture.configureByFile(getTestName(false) + ".kt")
assertInContexts(
KotlinTemplateContextType.Generic::class.java,
KotlinTemplateContextType.TopLevel::class.java)
assertInContexts(Generic::class.java, TopLevel::class.java)
}
fun testInExpression() {
myFixture.configureByFile(getTestName(false) + ".kt")
assertInContexts(
KotlinTemplateContextType.Generic::class.java,
KotlinTemplateContextType.Expression::class.java)
assertInContexts(Generic::class.java, Expression::class.java)
}
private fun assertInContexts(vararg expectedContexts: Class<out KotlinTemplateContextType>) {
fun testAnonymousObject() {
assertInContexts(Generic::class.java, Class::class.java)
}
fun testCompanionObject() {
assertInContexts(Generic::class.java, Class::class.java, ObjectDeclaration::class.java)
}
fun testLocalObject() {
assertInContexts(Generic::class.java, Class::class.java, ObjectDeclaration::class.java)
}
fun testObjectInClass() {
assertInContexts(Generic::class.java, Class::class.java, ObjectDeclaration::class.java)
}
fun testObjectInObject() {
assertInContexts(Generic::class.java, Class::class.java, ObjectDeclaration::class.java)
}
fun testTopLevelObject() {
assertInContexts(Generic::class.java, Class::class.java, ObjectDeclaration::class.java)
}
private fun assertInContexts(vararg expectedContexts: java.lang.Class<out KotlinTemplateContextType>) {
myFixture.configureByFile(getTestName(false) + ".kt")
val allContexts = TemplateContextType.EP_NAME.extensions.filter { it is KotlinTemplateContextType }
val enabledContexts = allContexts.filter { it.isInContext(myFixture.file, myFixture.caretOffset) }.map { it.javaClass }
UsefulTestCase.assertSameElements(enabledContexts, *expectedContexts)
+16
View File
@@ -8,6 +8,14 @@
</context>
</template>
<template resource-bundle="org.jetbrains.kotlin.idea.KotlinBundle" key="livetemplate.description.main"
name="maino" toReformat="true" toShortenFQNames="true"
value="@JvmStatic fun main(args : Array&lt;String&gt;) {&#10; $END$&#10;}">
<context>
<option name="KOTLIN_OBJECT_DECLARATION" value="true" />
</context>
</template>
<template resource-bundle="messages.CodeInsightBundle" key="livetemplate.description.sout"
name="sout" toReformat="true"
ShortenFQNames="true" value="println($END$)">
@@ -197,6 +205,14 @@
</context>
</template>
<template resource-bundle="org.jetbrains.kotlin.idea.KotlinBundle" key="livetemplate.description.main"
name="psvmo" toReformat="true" toShortenFQNames="true"
value="@JvmStatic fun main(args : Array&lt;String&gt;) {&#10; $END$&#10;}">
<context>
<option name="KOTLIN_OBJECT_DECLARATION" value="true" />
</context>
</template>
<!-- Synonym for anonymous -->
<template resource-bundle="org.jetbrains.kotlin.idea.KotlinBundle" key="livetemplate.description.anonymous"
name="object" id="kt-object" toReformat="true" toShortenFQNames="true" value="object : $SUPERTYPE$ {&#10;$END$&#10;}">
+1
View File
@@ -470,6 +470,7 @@
<liveTemplateContext implementation="org.jetbrains.kotlin.idea.liveTemplates.KotlinTemplateContextType$Class"/>
<liveTemplateContext implementation="org.jetbrains.kotlin.idea.liveTemplates.KotlinTemplateContextType$Expression"/>
<liveTemplateContext implementation="org.jetbrains.kotlin.idea.liveTemplates.KotlinTemplateContextType$Comment"/>
<liveTemplateContext implementation="org.jetbrains.kotlin.idea.liveTemplates.KotlinTemplateContextType$ObjectDeclaration"/>
<defaultLiveTemplatesProvider implementation="org.jetbrains.kotlin.idea.liveTemplates.KotlinLiveTemplatesProvider"/>
<liveTemplateMacro implementation="org.jetbrains.kotlin.idea.liveTemplates.macro.AnyVariableMacro"/>
<liveTemplateMacro implementation="org.jetbrains.kotlin.idea.liveTemplates.macro.SuitableVariableMacro"/>