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:
committed by
Nikolay Krasko
parent
48d7e8c519
commit
4d6206a7b5
@@ -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"
|
||||
|
||||
+70
-47
@@ -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
|
||||
}
|
||||
+30
-13
@@ -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)
|
||||
|
||||
@@ -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<String>) { $END$ }">
|
||||
<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<String>) { $END$ }">
|
||||
<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$ { $END$ }">
|
||||
|
||||
@@ -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"/>
|
||||
|
||||
Reference in New Issue
Block a user