203: Fix compilation for 203
This commit is contained in:
+55
@@ -0,0 +1,55 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||||
|
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||||
|
*/
|
||||||
|
package org.jetbrains.kotlin.cli.jvm.compiler;
|
||||||
|
|
||||||
|
import com.intellij.DynamicBundle;
|
||||||
|
import com.intellij.codeInsight.ContainerProvider;
|
||||||
|
import com.intellij.codeInsight.runner.JavaMainMethodProvider;
|
||||||
|
import com.intellij.core.JavaCoreApplicationEnvironment;
|
||||||
|
import com.intellij.lang.MetaLanguage;
|
||||||
|
import com.intellij.openapi.Disposable;
|
||||||
|
import com.intellij.openapi.extensions.Extensions;
|
||||||
|
import com.intellij.openapi.vfs.VirtualFileSystem;
|
||||||
|
import com.intellij.psi.FileContextProvider;
|
||||||
|
import com.intellij.psi.augment.PsiAugmentProvider;
|
||||||
|
import com.intellij.psi.compiled.ClassFileDecompilers;
|
||||||
|
import com.intellij.psi.meta.MetaDataContributor;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
import org.jetbrains.kotlin.cli.jvm.modules.CoreJrtFileSystem;
|
||||||
|
|
||||||
|
public class KotlinCoreApplicationEnvironment extends JavaCoreApplicationEnvironment {
|
||||||
|
public static KotlinCoreApplicationEnvironment create(@NotNull Disposable parentDisposable, boolean unitTestMode) {
|
||||||
|
KotlinCoreApplicationEnvironment environment = new KotlinCoreApplicationEnvironment(parentDisposable, unitTestMode);
|
||||||
|
registerExtensionPoints();
|
||||||
|
return environment;
|
||||||
|
}
|
||||||
|
|
||||||
|
private KotlinCoreApplicationEnvironment(@NotNull Disposable parentDisposable, boolean unitTestMode) {
|
||||||
|
super(parentDisposable, unitTestMode);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void registerExtensionPoints() {
|
||||||
|
registerApplicationExtensionPoint(DynamicBundle.LanguageBundleEP.EP_NAME, DynamicBundle.LanguageBundleEP.class);
|
||||||
|
registerApplicationExtensionPoint(FileContextProvider.EP_NAME, FileContextProvider.class);
|
||||||
|
|
||||||
|
registerApplicationExtensionPoint(MetaDataContributor.EP_NAME, MetaDataContributor.class);
|
||||||
|
registerApplicationExtensionPoint(PsiAugmentProvider.EP_NAME, PsiAugmentProvider.class);
|
||||||
|
registerApplicationExtensionPoint(JavaMainMethodProvider.EP_NAME, JavaMainMethodProvider.class);
|
||||||
|
|
||||||
|
registerApplicationExtensionPoint(ContainerProvider.EP_NAME, ContainerProvider.class);
|
||||||
|
registerApplicationExtensionPoint(ClassFileDecompilers.getInstance().EP_NAME, ClassFileDecompilers.Decompiler.class);
|
||||||
|
|
||||||
|
registerApplicationExtensionPoint(MetaLanguage.EP_NAME, MetaLanguage.class);
|
||||||
|
|
||||||
|
IdeaExtensionPoints.INSTANCE.registerVersionSpecificAppExtensionPoints(Extensions.getRootArea());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
protected VirtualFileSystem createJrtFileSystem() {
|
||||||
|
return new CoreJrtFileSystem();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,122 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||||
|
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.psi;
|
||||||
|
|
||||||
|
import com.intellij.extapi.psi.StubBasedPsiElementBase;
|
||||||
|
import com.intellij.lang.ASTNode;
|
||||||
|
import com.intellij.lang.Language;
|
||||||
|
import com.intellij.psi.*;
|
||||||
|
import com.intellij.psi.stubs.IStubElementType;
|
||||||
|
import com.intellij.psi.stubs.StubElement;
|
||||||
|
import com.intellij.util.IncorrectOperationException;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.kotlin.idea.KotlinLanguage;
|
||||||
|
import org.jetbrains.kotlin.psi.psiUtil.KtPsiUtilKt;
|
||||||
|
import org.jetbrains.kotlin.psi.stubs.elements.KtStubElementType;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class KtElementImplStub<T extends StubElement<?>> extends StubBasedPsiElementBase<T>
|
||||||
|
implements KtElement, StubBasedPsiElement<T> {
|
||||||
|
public KtElementImplStub(@NotNull T stub, @NotNull IStubElementType nodeType) {
|
||||||
|
super(stub, nodeType);
|
||||||
|
}
|
||||||
|
|
||||||
|
public KtElementImplStub(@NotNull ASTNode node) {
|
||||||
|
super(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public Language getLanguage() {
|
||||||
|
return KotlinLanguage.INSTANCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return getElementType().toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public final void accept(@NotNull PsiElementVisitor visitor) {
|
||||||
|
if (visitor instanceof KtVisitor) {
|
||||||
|
accept((KtVisitor) visitor, null);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
visitor.visitElement(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public KtFile getContainingKtFile() {
|
||||||
|
PsiFile file = getContainingFile();
|
||||||
|
if (!(file instanceof KtFile)) {
|
||||||
|
// KtElementImpl.copy() might be the reason for this exception
|
||||||
|
String fileString = file.isValid() ? (" " + file.getText()) : "";
|
||||||
|
throw new IllegalStateException("KtElement not inside KtFile: " + file + fileString +
|
||||||
|
" for element " + this + " of type " + this.getClass() + " node = " + getNode());
|
||||||
|
}
|
||||||
|
return (KtFile) file;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <D> void acceptChildren(@NotNull KtVisitor<Void, D> visitor, D data) {
|
||||||
|
PsiElement child = getFirstChild();
|
||||||
|
while (child != null) {
|
||||||
|
if (child instanceof KtElement) {
|
||||||
|
((KtElement) child).accept(visitor, data);
|
||||||
|
}
|
||||||
|
child = child.getNextSibling();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <R, D> R accept(@NotNull KtVisitor<R, D> visitor, D data) {
|
||||||
|
return visitor.visitKtElement(this, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void delete() throws IncorrectOperationException {
|
||||||
|
KtElementUtilsKt.deleteSemicolon(this);
|
||||||
|
super.delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
|
public PsiReference getReference() {
|
||||||
|
PsiReference[] references = getReferences();
|
||||||
|
if (references.length == 1) return references[0];
|
||||||
|
else return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public PsiReference[] getReferences() {
|
||||||
|
return KotlinReferenceProvidersService.getReferencesFromProviders(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
protected <PsiT extends KtElementImplStub<?>, StubT extends StubElement<?>> List<PsiT> getStubOrPsiChildrenAsList(
|
||||||
|
@NotNull KtStubElementType<StubT, PsiT> elementType
|
||||||
|
) {
|
||||||
|
return Arrays.asList(getStubOrPsiChildren(elementType, elementType.getArrayFactory()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public KtElement getPsiOrParent() {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PsiElement getParent() {
|
||||||
|
PsiElement substitute = KtPsiUtilKt.getParentSubstitute(this);
|
||||||
|
return substitute != null ? substitute : super.getParent();
|
||||||
|
}
|
||||||
|
}
|
||||||
+35
@@ -0,0 +1,35 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||||
|
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.psi.stubs.elements
|
||||||
|
|
||||||
|
import com.intellij.psi.PsiElement
|
||||||
|
import com.intellij.psi.stubs.StubElement
|
||||||
|
import com.intellij.psi.stubs.StubInputStream
|
||||||
|
import com.intellij.psi.stubs.StubOutputStream
|
||||||
|
import com.intellij.util.io.StringRef
|
||||||
|
import org.jetbrains.kotlin.psi.KtAnnotationUseSiteTarget
|
||||||
|
import org.jetbrains.kotlin.psi.stubs.KotlinAnnotationUseSiteTargetStub
|
||||||
|
import org.jetbrains.kotlin.psi.stubs.impl.KotlinAnnotationUseSiteTargetStubImpl
|
||||||
|
|
||||||
|
class KtAnnotationUseSiteTargetElementType(debugName: String) :
|
||||||
|
KtStubElementType<KotlinAnnotationUseSiteTargetStub, KtAnnotationUseSiteTarget>(
|
||||||
|
debugName, KtAnnotationUseSiteTarget::class.java, KotlinAnnotationUseSiteTargetStub::class.java
|
||||||
|
) {
|
||||||
|
|
||||||
|
override fun createStub(psi: KtAnnotationUseSiteTarget, parentStub: StubElement<out PsiElement>): KotlinAnnotationUseSiteTargetStub {
|
||||||
|
val useSiteTarget = psi.getAnnotationUseSiteTarget().name
|
||||||
|
return KotlinAnnotationUseSiteTargetStubImpl(parentStub, StringRef.fromString(useSiteTarget)!!)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun serialize(stub: KotlinAnnotationUseSiteTargetStub, dataStream: StubOutputStream) {
|
||||||
|
dataStream.writeName(stub.getUseSiteTarget())
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun deserialize(dataStream: StubInputStream, parentStub: StubElement<PsiElement>): KotlinAnnotationUseSiteTargetStub {
|
||||||
|
val useSiteTarget = dataStream.readName()
|
||||||
|
return KotlinAnnotationUseSiteTargetStubImpl(parentStub, useSiteTarget!!)
|
||||||
|
}
|
||||||
|
}
|
||||||
+31
@@ -0,0 +1,31 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||||
|
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.psi.stubs.elements
|
||||||
|
|
||||||
|
import com.intellij.psi.PsiElement
|
||||||
|
import com.intellij.psi.stubs.StubElement
|
||||||
|
import com.intellij.psi.stubs.StubInputStream
|
||||||
|
import com.intellij.psi.stubs.StubOutputStream
|
||||||
|
import com.intellij.util.io.StringRef
|
||||||
|
import org.jetbrains.kotlin.psi.KtImportAlias
|
||||||
|
import org.jetbrains.kotlin.psi.stubs.KotlinImportAliasStub
|
||||||
|
import org.jetbrains.kotlin.psi.stubs.impl.KotlinImportAliasStubImpl
|
||||||
|
|
||||||
|
class KtImportAliasElementType(debugName: String) :
|
||||||
|
KtStubElementType<KotlinImportAliasStub, KtImportAlias>(debugName, KtImportAlias::class.java, KotlinImportAliasStub::class.java) {
|
||||||
|
override fun createStub(psi: KtImportAlias, parentStub: StubElement<out PsiElement>?): KotlinImportAliasStub {
|
||||||
|
return KotlinImportAliasStubImpl(parentStub, StringRef.fromString(psi.name))
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun serialize(stub: KotlinImportAliasStub, dataStream: StubOutputStream) {
|
||||||
|
dataStream.writeName(stub.getName())
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun deserialize(dataStream: StubInputStream, parentStub: StubElement<PsiElement>?): KotlinImportAliasStub {
|
||||||
|
val name = dataStream.readName()
|
||||||
|
return KotlinImportAliasStubImpl(parentStub, name)
|
||||||
|
}
|
||||||
|
}
|
||||||
+44
@@ -0,0 +1,44 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||||
|
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.psi.stubs.elements;
|
||||||
|
|
||||||
|
import com.intellij.psi.stubs.StubElement;
|
||||||
|
import com.intellij.psi.stubs.StubInputStream;
|
||||||
|
import com.intellij.psi.stubs.StubOutputStream;
|
||||||
|
import com.intellij.util.io.DataInputOutputUtil;
|
||||||
|
import org.jetbrains.annotations.NonNls;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.kotlin.psi.KtModifierList;
|
||||||
|
import org.jetbrains.kotlin.psi.stubs.KotlinModifierListStub;
|
||||||
|
import org.jetbrains.kotlin.psi.stubs.impl.KotlinModifierListStubImpl;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import static org.jetbrains.kotlin.psi.stubs.impl.ModifierMaskUtils.computeMaskFromModifierList;
|
||||||
|
|
||||||
|
public class KtModifierListElementType<T extends KtModifierList> extends KtStubElementType<KotlinModifierListStub, T> {
|
||||||
|
public KtModifierListElementType(@NotNull @NonNls String debugName, @NotNull Class<T> psiClass) {
|
||||||
|
super(debugName, psiClass, KotlinModifierListStub.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public KotlinModifierListStub createStub(@NotNull T psi, StubElement<?> parentStub) {
|
||||||
|
return new KotlinModifierListStubImpl(parentStub, computeMaskFromModifierList(psi), this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void serialize(@NotNull KotlinModifierListStub stub, @NotNull StubOutputStream dataStream) throws IOException {
|
||||||
|
long mask = ((KotlinModifierListStubImpl) stub).getMask();
|
||||||
|
DataInputOutputUtil.writeLONG(dataStream, mask);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public KotlinModifierListStub deserialize(@NotNull StubInputStream dataStream, StubElement parentStub) throws IOException {
|
||||||
|
long mask = DataInputOutputUtil.readLONG(dataStream);
|
||||||
|
return new KotlinModifierListStubImpl(parentStub, mask, this);
|
||||||
|
}
|
||||||
|
}
|
||||||
+40
@@ -0,0 +1,40 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||||
|
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.psi.stubs.elements;
|
||||||
|
|
||||||
|
import com.intellij.psi.stubs.StubElement;
|
||||||
|
import com.intellij.psi.stubs.StubInputStream;
|
||||||
|
import com.intellij.psi.stubs.StubOutputStream;
|
||||||
|
import org.jetbrains.annotations.NonNls;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.kotlin.psi.KtElementImplStub;
|
||||||
|
import org.jetbrains.kotlin.psi.stubs.KotlinPlaceHolderStub;
|
||||||
|
import org.jetbrains.kotlin.psi.stubs.impl.KotlinPlaceHolderStubImpl;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
public class KtPlaceHolderStubElementType<T extends KtElementImplStub<? extends StubElement<?>>> extends
|
||||||
|
KtStubElementType<KotlinPlaceHolderStub<T>, T> {
|
||||||
|
public KtPlaceHolderStubElementType(@NotNull @NonNls String debugName, @NotNull Class<T> psiClass) {
|
||||||
|
super(debugName, psiClass, KotlinPlaceHolderStub.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public KotlinPlaceHolderStub<T> createStub(@NotNull T psi, StubElement<?> parentStub) {
|
||||||
|
return new KotlinPlaceHolderStubImpl<>(parentStub, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void serialize(@NotNull KotlinPlaceHolderStub<T> stub, @NotNull StubOutputStream dataStream) throws IOException {
|
||||||
|
//do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public KotlinPlaceHolderStub<T> deserialize(@NotNull StubInputStream dataStream, StubElement parentStub) throws IOException {
|
||||||
|
return new KotlinPlaceHolderStubImpl<>(parentStub, this);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||||
|
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.psi.stubs.elements
|
||||||
|
|
||||||
|
import com.intellij.psi.PsiElement
|
||||||
|
import com.intellij.psi.stubs.IndexSink
|
||||||
|
import com.intellij.psi.stubs.StubElement
|
||||||
|
import com.intellij.psi.stubs.StubInputStream
|
||||||
|
import com.intellij.psi.stubs.StubOutputStream
|
||||||
|
import com.intellij.util.io.StringRef
|
||||||
|
import org.jetbrains.kotlin.psi.KtScript
|
||||||
|
import org.jetbrains.kotlin.psi.stubs.KotlinScriptStub
|
||||||
|
import org.jetbrains.kotlin.psi.stubs.impl.KotlinScriptStubImpl
|
||||||
|
|
||||||
|
class KtScriptElementType(debugName: String) : KtStubElementType<KotlinScriptStub, KtScript>(
|
||||||
|
debugName, KtScript::class.java, KotlinScriptStub::class.java
|
||||||
|
) {
|
||||||
|
|
||||||
|
override fun createStub(psi: KtScript, parentStub: StubElement<out PsiElement>): KotlinScriptStub {
|
||||||
|
return KotlinScriptStubImpl(parentStub, StringRef.fromString(psi.fqName.asString()))
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun serialize(stub: KotlinScriptStub, dataStream: StubOutputStream) {
|
||||||
|
dataStream.writeName(stub.getFqName().asString())
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun deserialize(dataStream: StubInputStream, parentStub: StubElement<PsiElement>): KotlinScriptStub {
|
||||||
|
val fqName = dataStream.readName()
|
||||||
|
return KotlinScriptStubImpl(parentStub, fqName)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
override fun indexStub(stub: KotlinScriptStub, sink: IndexSink) {
|
||||||
|
StubIndexService.getInstance().indexScript(stub, sink)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,108 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||||
|
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.psi.stubs.elements;
|
||||||
|
|
||||||
|
import com.intellij.lang.ASTNode;
|
||||||
|
import com.intellij.psi.PsiElement;
|
||||||
|
import com.intellij.psi.stubs.IStubElementType;
|
||||||
|
import com.intellij.psi.stubs.IndexSink;
|
||||||
|
import com.intellij.psi.stubs.StubElement;
|
||||||
|
import com.intellij.psi.tree.IElementType;
|
||||||
|
import com.intellij.psi.tree.IStubFileElementType;
|
||||||
|
import com.intellij.util.ArrayFactory;
|
||||||
|
import com.intellij.util.ReflectionUtil;
|
||||||
|
import org.jetbrains.annotations.NonNls;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.kotlin.idea.KotlinLanguage;
|
||||||
|
import org.jetbrains.kotlin.psi.KtClassOrObject;
|
||||||
|
import org.jetbrains.kotlin.psi.KtElementImplStub;
|
||||||
|
import org.jetbrains.kotlin.psi.KtFunction;
|
||||||
|
import org.jetbrains.kotlin.psi.KtProperty;
|
||||||
|
|
||||||
|
import java.lang.reflect.Array;
|
||||||
|
import java.lang.reflect.Constructor;
|
||||||
|
|
||||||
|
public abstract class KtStubElementType<StubT extends StubElement<?>, PsiT extends KtElementImplStub<?>> extends IStubElementType<StubT, PsiT> {
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
private final Constructor<PsiT> byNodeConstructor;
|
||||||
|
@NotNull
|
||||||
|
private final Constructor<PsiT> byStubConstructor;
|
||||||
|
@NotNull
|
||||||
|
private final PsiT[] emptyArray;
|
||||||
|
@NotNull
|
||||||
|
private final ArrayFactory<PsiT> arrayFactory;
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public KtStubElementType(@NotNull @NonNls String debugName, @NotNull Class<PsiT> psiClass, @NotNull Class<?> stubClass) {
|
||||||
|
super(debugName, KotlinLanguage.INSTANCE);
|
||||||
|
try {
|
||||||
|
byNodeConstructor = psiClass.getConstructor(ASTNode.class);
|
||||||
|
byStubConstructor = psiClass.getConstructor(stubClass);
|
||||||
|
}
|
||||||
|
catch (NoSuchMethodException e) {
|
||||||
|
throw new RuntimeException("Stub element type declaration for " + psiClass.getSimpleName() + " is missing required constructors",e);
|
||||||
|
}
|
||||||
|
emptyArray = (PsiT[]) Array.newInstance(psiClass, 0);
|
||||||
|
arrayFactory = count -> {
|
||||||
|
if (count == 0) {
|
||||||
|
return emptyArray;
|
||||||
|
}
|
||||||
|
return (PsiT[]) Array.newInstance(psiClass, count);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public PsiT createPsiFromAst(@NotNull ASTNode node) {
|
||||||
|
return ReflectionUtil.createInstance(byNodeConstructor, node);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@NotNull
|
||||||
|
public PsiT createPsi(@NotNull StubT stub) {
|
||||||
|
return ReflectionUtil.createInstance(byStubConstructor, stub);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public String getExternalId() {
|
||||||
|
return "kotlin." + toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean shouldCreateStub(ASTNode node) {
|
||||||
|
PsiElement psi = node.getPsi();
|
||||||
|
if (psi instanceof KtClassOrObject || psi instanceof KtFunction) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (psi instanceof KtProperty) {
|
||||||
|
return !((KtProperty) psi).isLocal();
|
||||||
|
}
|
||||||
|
return createStubDependingOnParent(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean createStubDependingOnParent(ASTNode node) {
|
||||||
|
ASTNode parent = node.getTreeParent();
|
||||||
|
IElementType parentType = parent.getElementType();
|
||||||
|
if (parentType instanceof IStubElementType) {
|
||||||
|
return ((IStubElementType) parentType).shouldCreateStub(parent);
|
||||||
|
}
|
||||||
|
if (parentType instanceof IStubFileElementType) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void indexStub(@NotNull StubT stub, @NotNull IndexSink sink) {
|
||||||
|
// do not force inheritors to implement this method
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public ArrayFactory<PsiT> getArrayFactory() {
|
||||||
|
return arrayFactory;
|
||||||
|
}
|
||||||
|
}
|
||||||
+31
@@ -0,0 +1,31 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||||
|
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.psi.stubs.elements
|
||||||
|
|
||||||
|
import com.intellij.psi.PsiElement
|
||||||
|
import com.intellij.psi.stubs.StubElement
|
||||||
|
import com.intellij.psi.stubs.StubInputStream
|
||||||
|
import com.intellij.psi.stubs.StubOutputStream
|
||||||
|
import org.jetbrains.kotlin.psi.KtValueArgument
|
||||||
|
import org.jetbrains.kotlin.psi.stubs.KotlinValueArgumentStub
|
||||||
|
import org.jetbrains.kotlin.psi.stubs.impl.KotlinValueArgumentStubImpl
|
||||||
|
|
||||||
|
class KtValueArgumentElementType<T : KtValueArgument>(debugName: String, psiClass: Class<T>) :
|
||||||
|
KtStubElementType<KotlinValueArgumentStub<T>, T>(debugName, psiClass, KotlinValueArgumentStub::class.java) {
|
||||||
|
|
||||||
|
override fun createStub(psi: T, parentStub: StubElement<out PsiElement>?): KotlinValueArgumentStub<T> {
|
||||||
|
return KotlinValueArgumentStubImpl(parentStub, this, psi.isSpread)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun serialize(stub: KotlinValueArgumentStub<T>, dataStream: StubOutputStream) {
|
||||||
|
dataStream.writeBoolean(stub.isSpread())
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun deserialize(dataStream: StubInputStream, parentStub: StubElement<PsiElement>?): KotlinValueArgumentStub<T> {
|
||||||
|
val isSpread = dataStream.readBoolean()
|
||||||
|
return KotlinValueArgumentStubImpl(parentStub, this, isSpread)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||||
|
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.psi.stubs.impl
|
||||||
|
|
||||||
|
import com.intellij.psi.PsiElement
|
||||||
|
import com.intellij.psi.stubs.StubElement
|
||||||
|
import com.intellij.util.io.StringRef
|
||||||
|
import org.jetbrains.kotlin.psi.KtImportAlias
|
||||||
|
import org.jetbrains.kotlin.psi.stubs.KotlinImportAliasStub
|
||||||
|
import org.jetbrains.kotlin.psi.stubs.elements.KtStubElementTypes
|
||||||
|
|
||||||
|
class KotlinImportAliasStubImpl(
|
||||||
|
parent: StubElement<out PsiElement>?,
|
||||||
|
private val name: StringRef?
|
||||||
|
) : KotlinStubBaseImpl<KtImportAlias>(parent, KtStubElementTypes.IMPORT_ALIAS), KotlinImportAliasStub {
|
||||||
|
override fun getName(): String? = StringRef.toString(name)
|
||||||
|
}
|
||||||
+1178
File diff suppressed because it is too large
Load Diff
+37
@@ -0,0 +1,37 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||||
|
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.idea.completion.test;
|
||||||
|
|
||||||
|
import com.intellij.codeInsight.CodeInsightSettings;
|
||||||
|
import com.intellij.codeInsight.completion.CompletionTestCase;
|
||||||
|
import com.intellij.openapi.vfs.newvfs.impl.VfsRootAccess;
|
||||||
|
import com.intellij.util.ArrayUtil;
|
||||||
|
import com.intellij.util.ThrowableRunnable;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.kotlin.test.KotlinTestUtils;
|
||||||
|
import org.jetbrains.kotlin.test.WithMutedInDatabaseRunTest;
|
||||||
|
|
||||||
|
@WithMutedInDatabaseRunTest
|
||||||
|
abstract public class KotlinCompletionTestCase extends CompletionTestCase {
|
||||||
|
@Override
|
||||||
|
protected void setUp() throws Exception {
|
||||||
|
super.setUp();
|
||||||
|
VfsRootAccess.allowRootAccess(KotlinTestUtils.getHomeDirectory());
|
||||||
|
CodeInsightSettings.getInstance().EXCLUDED_PACKAGES = new String[]{"excludedPackage", "somePackage.ExcludedClass"};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void tearDown() throws Exception {
|
||||||
|
CodeInsightSettings.getInstance().EXCLUDED_PACKAGES = ArrayUtil.EMPTY_STRING_ARRAY;
|
||||||
|
VfsRootAccess.disallowRootAccess(KotlinTestUtils.getHomeDirectory());
|
||||||
|
super.tearDown();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void runTestRunnable(@NotNull ThrowableRunnable<Throwable> testRunnable) throws Throwable {
|
||||||
|
KotlinTestUtils.runTestWithThrowable(this, () -> super.runTestRunnable(testRunnable));
|
||||||
|
}
|
||||||
|
}
|
||||||
+37
@@ -0,0 +1,37 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||||
|
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.idea.test;
|
||||||
|
|
||||||
|
import com.intellij.codeInsight.CodeInsightTestCase;
|
||||||
|
import com.intellij.openapi.vfs.newvfs.impl.VfsRootAccess;
|
||||||
|
import com.intellij.util.ThrowableRunnable;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.kotlin.test.KotlinTestUtils;
|
||||||
|
import org.jetbrains.kotlin.test.WithMutedInDatabaseRunTest;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Please use KotlinLightCodeInsightFixtureTestCase as the base class for all new tests.
|
||||||
|
*/
|
||||||
|
@WithMutedInDatabaseRunTest
|
||||||
|
@Deprecated
|
||||||
|
public abstract class KotlinCodeInsightTestCase extends CodeInsightTestCase {
|
||||||
|
@Override
|
||||||
|
protected void setUp() throws Exception {
|
||||||
|
VfsRootAccess.allowRootAccess(KotlinTestUtils.getHomeDirectory());
|
||||||
|
super.setUp();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void tearDown() throws Exception {
|
||||||
|
super.tearDown();
|
||||||
|
VfsRootAccess.disallowRootAccess(KotlinTestUtils.getHomeDirectory());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void runTestRunnable(@NotNull ThrowableRunnable<Throwable> testRunnable) throws Throwable {
|
||||||
|
KotlinTestUtils.runTestWithThrowable(this, () -> super.runTestRunnable(testRunnable));
|
||||||
|
}
|
||||||
|
}
|
||||||
+99
@@ -0,0 +1,99 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||||
|
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.idea.test;
|
||||||
|
|
||||||
|
import com.intellij.openapi.editor.Editor;
|
||||||
|
import com.intellij.openapi.project.Project;
|
||||||
|
import com.intellij.openapi.util.io.FileUtil;
|
||||||
|
import com.intellij.openapi.vfs.LocalFileSystem;
|
||||||
|
import com.intellij.openapi.vfs.VirtualFile;
|
||||||
|
import com.intellij.psi.PsiFile;
|
||||||
|
import com.intellij.testFramework.TempFiles;
|
||||||
|
import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase;
|
||||||
|
import com.intellij.util.ThrowableRunnable;
|
||||||
|
import gnu.trove.THashSet;
|
||||||
|
import org.jetbrains.annotations.NonNls;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
import org.jetbrains.kotlin.test.KotlinTestUtils;
|
||||||
|
import org.jetbrains.kotlin.test.WithMutedInDatabaseRunTest;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.OutputStreamWriter;
|
||||||
|
import java.nio.charset.Charset;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
|
@WithMutedInDatabaseRunTest
|
||||||
|
public abstract class KotlinLightCodeInsightFixtureTestCaseBase extends LightCodeInsightFixtureTestCase {
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public Project getProject() {
|
||||||
|
return super.getProject();
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public Editor getEditor() {
|
||||||
|
return super.getEditor();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PsiFile getFile() {
|
||||||
|
return super.getFile();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected final Collection<Path> myFilesToDelete = new THashSet<>();
|
||||||
|
private final TempFiles myTempFiles = new TempFiles(myFilesToDelete);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void tearDown() throws Exception {
|
||||||
|
myTempFiles.deleteAll();
|
||||||
|
super.tearDown();
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public VirtualFile createTempFile(
|
||||||
|
@NonNls @NotNull String ext,
|
||||||
|
@Nullable byte[] bom,
|
||||||
|
@NonNls @NotNull String content,
|
||||||
|
@NotNull Charset charset
|
||||||
|
) throws IOException {
|
||||||
|
File temp = FileUtil.createTempFile("copy", "." + ext);
|
||||||
|
setContentOnDisk(temp, bom, content, charset);
|
||||||
|
|
||||||
|
myFilesToDelete.add(temp.toPath());
|
||||||
|
final VirtualFile file = getVirtualFile(temp);
|
||||||
|
assert file != null : temp;
|
||||||
|
return file;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void setContentOnDisk(@NotNull File file, @Nullable byte[] bom, @NotNull String content, @NotNull Charset charset)
|
||||||
|
throws IOException {
|
||||||
|
FileOutputStream stream = new FileOutputStream(file);
|
||||||
|
if (bom != null) {
|
||||||
|
stream.write(bom);
|
||||||
|
}
|
||||||
|
try (OutputStreamWriter writer = new OutputStreamWriter(stream, charset)) {
|
||||||
|
writer.write(content);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static VirtualFile getVirtualFile(@NotNull File file) {
|
||||||
|
return LocalFileSystem.getInstance().refreshAndFindFileByIoFile(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void runTestRunnable(@NotNull ThrowableRunnable<Throwable> testRunnable) throws Throwable {
|
||||||
|
KotlinTestUtils.runTestWithThrowable(this, () -> super.runTestRunnable(testRunnable));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean isFirPlugin() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,263 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||||
|
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.idea.actions
|
||||||
|
|
||||||
|
import com.intellij.ide.actions.CreateFileFromTemplateAction
|
||||||
|
import com.intellij.ide.actions.CreateFileFromTemplateDialog
|
||||||
|
import com.intellij.ide.actions.CreateFromTemplateAction
|
||||||
|
import com.intellij.ide.fileTemplates.FileTemplate
|
||||||
|
import com.intellij.ide.fileTemplates.FileTemplateManager
|
||||||
|
import com.intellij.ide.fileTemplates.actions.AttributesDefaults
|
||||||
|
import com.intellij.ide.fileTemplates.ui.CreateFromTemplateDialog
|
||||||
|
import com.intellij.openapi.actionSystem.DataContext
|
||||||
|
import com.intellij.openapi.actionSystem.LangDataKeys
|
||||||
|
import com.intellij.openapi.actionSystem.PlatformDataKeys
|
||||||
|
import com.intellij.openapi.editor.LogicalPosition
|
||||||
|
import com.intellij.openapi.extensions.ExtensionPointName
|
||||||
|
import com.intellij.openapi.fileEditor.FileEditorManager
|
||||||
|
import com.intellij.openapi.module.Module
|
||||||
|
import com.intellij.openapi.module.ModuleUtilCore
|
||||||
|
import com.intellij.openapi.project.DumbAware
|
||||||
|
import com.intellij.openapi.project.DumbService
|
||||||
|
import com.intellij.openapi.project.Project
|
||||||
|
import com.intellij.openapi.roots.ProjectRootManager
|
||||||
|
import com.intellij.openapi.ui.InputValidatorEx
|
||||||
|
import com.intellij.psi.PsiDirectory
|
||||||
|
import com.intellij.psi.PsiFile
|
||||||
|
import com.intellij.util.IncorrectOperationException
|
||||||
|
import org.jetbrains.annotations.TestOnly
|
||||||
|
import org.jetbrains.kotlin.idea.KotlinBundle
|
||||||
|
import org.jetbrains.kotlin.idea.KotlinFileType
|
||||||
|
import org.jetbrains.kotlin.idea.KotlinIcons
|
||||||
|
import org.jetbrains.kotlin.idea.statistics.FUSEventGroups
|
||||||
|
import org.jetbrains.kotlin.idea.statistics.KotlinFUSLogger
|
||||||
|
import org.jetbrains.kotlin.idea.util.application.runWriteAction
|
||||||
|
import org.jetbrains.kotlin.lexer.KtTokens
|
||||||
|
import org.jetbrains.kotlin.parsing.KotlinParserDefinition.Companion.STD_SCRIPT_SUFFIX
|
||||||
|
import org.jetbrains.kotlin.psi.KtClass
|
||||||
|
import org.jetbrains.kotlin.psi.KtFile
|
||||||
|
import org.jetbrains.kotlin.psi.KtNamedDeclaration
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
class NewKotlinFileAction : CreateFileFromTemplateAction(
|
||||||
|
KotlinBundle.message("action.new.file.text"),
|
||||||
|
KotlinBundle.message("action.new.file.description"),
|
||||||
|
KotlinFileType.INSTANCE.icon
|
||||||
|
), DumbAware {
|
||||||
|
override fun postProcess(createdElement: PsiFile, templateName: String?, customProperties: Map<String, String>?) {
|
||||||
|
super.postProcess(createdElement, templateName, customProperties)
|
||||||
|
|
||||||
|
val module = ModuleUtilCore.findModuleForPsiElement(createdElement!!)
|
||||||
|
|
||||||
|
if (createdElement is KtFile) {
|
||||||
|
if (module != null) {
|
||||||
|
for (hook in NewKotlinFileHook.EP_NAME.extensions) {
|
||||||
|
hook.postProcess(createdElement, module)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val ktClass = createdElement.declarations.singleOrNull() as? KtNamedDeclaration
|
||||||
|
if (ktClass != null) {
|
||||||
|
CreateFromTemplateAction.moveCaretAfterNameIdentifier(ktClass)
|
||||||
|
} else {
|
||||||
|
val editor = FileEditorManager.getInstance(createdElement.project).selectedTextEditor ?: return
|
||||||
|
if (editor.document == createdElement.viewProvider.document) {
|
||||||
|
val lineCount = editor.document.lineCount
|
||||||
|
if (lineCount > 0) {
|
||||||
|
editor.caretModel.moveToLogicalPosition(LogicalPosition(lineCount - 1, 0))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun buildDialog(project: Project, directory: PsiDirectory, builder: CreateFileFromTemplateDialog.Builder) {
|
||||||
|
builder.setTitle(KotlinBundle.message("action.new.file.dialog.title"))
|
||||||
|
.addKind(
|
||||||
|
KotlinBundle.message("action.new.file.dialog.class.title"),
|
||||||
|
KotlinIcons.CLASS,
|
||||||
|
"Kotlin Class"
|
||||||
|
)
|
||||||
|
.addKind(
|
||||||
|
KotlinBundle.message("action.new.file.dialog.file.title"),
|
||||||
|
KotlinFileType.INSTANCE.icon,
|
||||||
|
"Kotlin File"
|
||||||
|
)
|
||||||
|
.addKind(
|
||||||
|
KotlinBundle.message("action.new.file.dialog.interface.title"),
|
||||||
|
KotlinIcons.INTERFACE,
|
||||||
|
"Kotlin Interface"
|
||||||
|
)
|
||||||
|
.addKind(
|
||||||
|
KotlinBundle.message("action.new.file.dialog.data.class.title"),
|
||||||
|
KotlinIcons.CLASS,
|
||||||
|
"Kotlin Data Class"
|
||||||
|
)
|
||||||
|
.addKind(
|
||||||
|
KotlinBundle.message("action.new.file.dialog.enum.title"),
|
||||||
|
KotlinIcons.ENUM,
|
||||||
|
"Kotlin Enum"
|
||||||
|
)
|
||||||
|
.addKind(
|
||||||
|
KotlinBundle.message("action.new.file.dialog.sealed.class.title"),
|
||||||
|
KotlinIcons.CLASS,
|
||||||
|
"Kotlin Sealed Class"
|
||||||
|
)
|
||||||
|
.addKind(
|
||||||
|
KotlinBundle.message("action.new.file.dialog.annotation.title"),
|
||||||
|
KotlinIcons.ANNOTATION,
|
||||||
|
"Kotlin Annotation"
|
||||||
|
)
|
||||||
|
.addKind(
|
||||||
|
KotlinBundle.message("action.new.file.dialog.object.title"),
|
||||||
|
KotlinIcons.OBJECT,
|
||||||
|
"Kotlin Object"
|
||||||
|
)
|
||||||
|
|
||||||
|
builder.setValidator(NameValidator)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getActionName(directory: PsiDirectory, newName: String, templateName: String): String =
|
||||||
|
KotlinBundle.message("action.new.file.text")
|
||||||
|
|
||||||
|
override fun isAvailable(dataContext: DataContext): Boolean {
|
||||||
|
if (super.isAvailable(dataContext)) {
|
||||||
|
val ideView = LangDataKeys.IDE_VIEW.getData(dataContext)!!
|
||||||
|
val project = PlatformDataKeys.PROJECT.getData(dataContext)!!
|
||||||
|
val projectFileIndex = ProjectRootManager.getInstance(project).fileIndex
|
||||||
|
return ideView.directories.any { projectFileIndex.isInSourceContent(it.virtualFile) }
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun hashCode(): Int = 0
|
||||||
|
|
||||||
|
override fun equals(other: Any?): Boolean = other is NewKotlinFileAction
|
||||||
|
|
||||||
|
override fun startInWriteAction() = false
|
||||||
|
|
||||||
|
override fun createFileFromTemplate(name: String, template: FileTemplate, dir: PsiDirectory) =
|
||||||
|
createFileFromTemplateWithStat(name, template, dir)
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private object NameValidator : InputValidatorEx {
|
||||||
|
override fun getErrorText(inputString: String): String? {
|
||||||
|
if (inputString.trim().isEmpty()) {
|
||||||
|
return KotlinBundle.message("action.new.file.error.empty.name")
|
||||||
|
}
|
||||||
|
|
||||||
|
val parts: List<String> = inputString.split(*FQNAME_SEPARATORS)
|
||||||
|
if (parts.any { it.trim().isEmpty() }) {
|
||||||
|
return KotlinBundle.message("action.new.file.error.empty.name.part")
|
||||||
|
}
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun checkInput(inputString: String): Boolean = true
|
||||||
|
|
||||||
|
override fun canClose(inputString: String): Boolean = getErrorText(inputString) == null
|
||||||
|
}
|
||||||
|
|
||||||
|
@get:TestOnly
|
||||||
|
val nameValidator: InputValidatorEx
|
||||||
|
get() = NameValidator
|
||||||
|
|
||||||
|
private fun findOrCreateTarget(dir: PsiDirectory, name: String, directorySeparators: CharArray): Pair<String, PsiDirectory> {
|
||||||
|
var className = removeKotlinExtensionIfPresent(name)
|
||||||
|
var targetDir = dir
|
||||||
|
|
||||||
|
for (splitChar in directorySeparators) {
|
||||||
|
if (splitChar in className) {
|
||||||
|
val names = className.trim().split(splitChar)
|
||||||
|
|
||||||
|
for (dirName in names.dropLast(1)) {
|
||||||
|
targetDir = targetDir.findSubdirectory(dirName) ?: runWriteAction {
|
||||||
|
targetDir.createSubdirectory(dirName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
className = names.last()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Pair(className, targetDir)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun removeKotlinExtensionIfPresent(name: String): String = when {
|
||||||
|
name.endsWith(".$KOTLIN_WORKSHEET_EXTENSION") -> name.removeSuffix(".$KOTLIN_WORKSHEET_EXTENSION")
|
||||||
|
name.endsWith(".$STD_SCRIPT_SUFFIX") -> name.removeSuffix(".$STD_SCRIPT_SUFFIX")
|
||||||
|
name.endsWith(".${KotlinFileType.EXTENSION}") -> name.removeSuffix(".${KotlinFileType.EXTENSION}")
|
||||||
|
else -> name
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createFromTemplate(dir: PsiDirectory, className: String, template: FileTemplate): PsiFile? {
|
||||||
|
val project = dir.project
|
||||||
|
val defaultProperties = FileTemplateManager.getInstance(project).defaultProperties
|
||||||
|
|
||||||
|
val properties = Properties(defaultProperties)
|
||||||
|
|
||||||
|
val element = try {
|
||||||
|
CreateFromTemplateDialog(
|
||||||
|
project, dir, template,
|
||||||
|
AttributesDefaults(className).withFixedName(true),
|
||||||
|
properties
|
||||||
|
).create()
|
||||||
|
} catch (e: IncorrectOperationException) {
|
||||||
|
throw e
|
||||||
|
} catch (e: Exception) {
|
||||||
|
LOG.error(e)
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
return element?.containingFile
|
||||||
|
}
|
||||||
|
|
||||||
|
private val FILE_SEPARATORS = charArrayOf('/', '\\')
|
||||||
|
private val FQNAME_SEPARATORS = charArrayOf('/', '\\', '.')
|
||||||
|
|
||||||
|
fun createFileFromTemplateWithStat(name: String, template: FileTemplate, dir: PsiDirectory): PsiFile? {
|
||||||
|
KotlinFUSLogger.log(FUSEventGroups.NewFileTemplate, template.name)
|
||||||
|
return createFileFromTemplate(name, template, dir)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fun createFileFromTemplate(name: String, template: FileTemplate, dir: PsiDirectory): PsiFile? {
|
||||||
|
val directorySeparators = when (template.name) {
|
||||||
|
"Kotlin File" -> FILE_SEPARATORS
|
||||||
|
else -> FQNAME_SEPARATORS
|
||||||
|
}
|
||||||
|
val (className, targetDir) = findOrCreateTarget(dir, name, directorySeparators)
|
||||||
|
|
||||||
|
val service = DumbService.getInstance(dir.project)
|
||||||
|
service.isAlternativeResolveEnabled = true
|
||||||
|
try {
|
||||||
|
val psiFile = createFromTemplate(targetDir, className, template)
|
||||||
|
if (psiFile is KtFile) {
|
||||||
|
val singleClass = psiFile.declarations.singleOrNull() as? KtClass
|
||||||
|
if (singleClass != null && !singleClass.isEnum() && !singleClass.isInterface() && name.contains("Abstract")) {
|
||||||
|
runWriteAction {
|
||||||
|
singleClass.addModifier(KtTokens.ABSTRACT_KEYWORD)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return psiFile
|
||||||
|
} finally {
|
||||||
|
service.isAlternativeResolveEnabled = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class NewKotlinFileHook {
|
||||||
|
companion object {
|
||||||
|
val EP_NAME: ExtensionPointName<NewKotlinFileHook> =
|
||||||
|
ExtensionPointName.create<NewKotlinFileHook>("org.jetbrains.kotlin.newFileHook")
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract fun postProcess(createdElement: KtFile, module: Module)
|
||||||
|
}
|
||||||
+38
@@ -0,0 +1,38 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||||
|
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.idea.configuration
|
||||||
|
|
||||||
|
import com.intellij.openapi.util.io.FileUtil
|
||||||
|
import org.jetbrains.kotlin.codegen.forTestCompile.ForTestCompileRuntime
|
||||||
|
import org.junit.Assert
|
||||||
|
import java.io.File
|
||||||
|
import java.nio.file.Path
|
||||||
|
|
||||||
|
abstract class AbstractConfigureKotlinInTempDirTest : AbstractConfigureKotlinTest() {
|
||||||
|
override fun getProjectDirOrFile(): Path {
|
||||||
|
val tempDir = FileUtil.generateRandomTemporaryPath()
|
||||||
|
FileUtil.createTempDirectory("temp", null)
|
||||||
|
myFilesToDelete.add(tempDir.toPath())
|
||||||
|
|
||||||
|
FileUtil.copyDir(File(projectRoot), tempDir)
|
||||||
|
|
||||||
|
val kotlinRuntime = File(tempDir, "lib/kotlin-stdlib.jar")
|
||||||
|
if (getTestName(true).toLowerCase().contains("latestruntime") && kotlinRuntime.exists()) {
|
||||||
|
ForTestCompileRuntime.runtimeJarForTests().copyTo(kotlinRuntime, overwrite = true)
|
||||||
|
}
|
||||||
|
|
||||||
|
val projectRoot = tempDir.path
|
||||||
|
|
||||||
|
val projectFilePath = projectRoot + "/projectFile.ipr"
|
||||||
|
if (!File(projectFilePath).exists()) {
|
||||||
|
val dotIdeaPath = projectRoot + "/.idea"
|
||||||
|
Assert.assertTrue("Project file or '.idea' dir should exists in " + projectRoot, File(dotIdeaPath).exists())
|
||||||
|
return File(projectRoot).toPath()
|
||||||
|
}
|
||||||
|
|
||||||
|
return File(projectFilePath).toPath()
|
||||||
|
}
|
||||||
|
}
|
||||||
+256
@@ -0,0 +1,256 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||||
|
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.idea.configuration
|
||||||
|
|
||||||
|
import com.intellij.openapi.application.ApplicationManager
|
||||||
|
import com.intellij.openapi.application.PathMacros
|
||||||
|
import com.intellij.openapi.module.Module
|
||||||
|
import com.intellij.openapi.module.ModuleManager
|
||||||
|
import com.intellij.openapi.project.Project
|
||||||
|
import com.intellij.openapi.projectRoots.Sdk
|
||||||
|
import com.intellij.openapi.roots.ProjectRootManager
|
||||||
|
import com.intellij.openapi.util.io.FileUtil
|
||||||
|
import com.intellij.openapi.util.io.FileUtilRt
|
||||||
|
import com.intellij.openapi.vfs.newvfs.impl.VfsRootAccess
|
||||||
|
import com.intellij.testFramework.PlatformTestCase
|
||||||
|
import com.intellij.testFramework.UsefulTestCase
|
||||||
|
import com.intellij.util.ThrowableRunnable
|
||||||
|
import junit.framework.TestCase
|
||||||
|
import org.jetbrains.kotlin.idea.configuration.KotlinWithLibraryConfigurator.FileState
|
||||||
|
import org.jetbrains.kotlin.idea.framework.KotlinSdkType
|
||||||
|
import org.jetbrains.kotlin.idea.test.PluginTestCaseBase.*
|
||||||
|
import org.jetbrains.kotlin.test.KotlinTestUtils
|
||||||
|
import org.jetbrains.kotlin.test.WithMutedInDatabaseRunTest
|
||||||
|
import org.jetbrains.kotlin.test.runTest
|
||||||
|
import org.jetbrains.kotlin.utils.PathUtil
|
||||||
|
import java.io.File
|
||||||
|
import java.nio.file.Path
|
||||||
|
import java.nio.file.Paths
|
||||||
|
|
||||||
|
@WithMutedInDatabaseRunTest
|
||||||
|
abstract class AbstractConfigureKotlinTest : PlatformTestCase() {
|
||||||
|
override fun setUp() {
|
||||||
|
super.setUp()
|
||||||
|
|
||||||
|
VfsRootAccess.allowRootAccess(KotlinTestUtils.getHomeDirectory())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Throws(Exception::class)
|
||||||
|
override fun tearDown() {
|
||||||
|
VfsRootAccess.disallowRootAccess(KotlinTestUtils.getHomeDirectory())
|
||||||
|
PathMacros.getInstance().removeMacro(TEMP_DIR_MACRO_KEY)
|
||||||
|
|
||||||
|
super.tearDown()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Throws(Exception::class)
|
||||||
|
override fun initApplication() {
|
||||||
|
super.initApplication()
|
||||||
|
|
||||||
|
KotlinSdkType.setUpIfNeeded(testRootDisposable)
|
||||||
|
|
||||||
|
ApplicationManager.getApplication().runWriteAction {
|
||||||
|
addJdk(testRootDisposable, ::mockJdk6)
|
||||||
|
addJdk(testRootDisposable, ::mockJdk8)
|
||||||
|
addJdk(testRootDisposable, ::mockJdk9)
|
||||||
|
}
|
||||||
|
|
||||||
|
val tempLibDir = FileUtil.createTempDirectory("temp", null)
|
||||||
|
PathMacros.getInstance().setMacro(TEMP_DIR_MACRO_KEY, FileUtilRt.toSystemDependentName(tempLibDir.absolutePath))
|
||||||
|
}
|
||||||
|
|
||||||
|
protected fun doTestConfigureModulesWithNonDefaultSetup(configurator: KotlinWithLibraryConfigurator) {
|
||||||
|
assertNoFilesInDefaultPaths()
|
||||||
|
|
||||||
|
val modules = modules
|
||||||
|
for (module in modules) {
|
||||||
|
assertNotConfigured(module, configurator)
|
||||||
|
}
|
||||||
|
|
||||||
|
configurator.configure(myProject, emptyList())
|
||||||
|
|
||||||
|
assertNoFilesInDefaultPaths()
|
||||||
|
|
||||||
|
for (module in modules) {
|
||||||
|
assertProperlyConfigured(module, configurator)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected fun doTestOneJavaModule(jarState: FileState) {
|
||||||
|
doTestOneModule(jarState, JAVA_CONFIGURATOR)
|
||||||
|
}
|
||||||
|
|
||||||
|
protected fun doTestOneJsModule(jarState: FileState) {
|
||||||
|
doTestOneModule(jarState, JS_CONFIGURATOR)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun doTestOneModule(jarState: FileState, configurator: KotlinWithLibraryConfigurator) {
|
||||||
|
val module = module
|
||||||
|
|
||||||
|
assertNotConfigured(module, configurator)
|
||||||
|
configure(module, jarState, configurator)
|
||||||
|
assertProperlyConfigured(module, configurator)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getModule(): Module {
|
||||||
|
val modules = ModuleManager.getInstance(myProject).modules
|
||||||
|
assert(modules.size == 1) { "One module should be loaded " + modules.size }
|
||||||
|
myModule = modules[0]
|
||||||
|
return super.getModule()
|
||||||
|
}
|
||||||
|
|
||||||
|
val modules: Array<Module>
|
||||||
|
get() = ModuleManager.getInstance(myProject).modules
|
||||||
|
|
||||||
|
override fun getProjectDirOrFile(): Path {
|
||||||
|
val projectFilePath = projectRoot + "/projectFile.ipr"
|
||||||
|
TestCase.assertTrue("Project file should exists " + projectFilePath, File(projectFilePath).exists())
|
||||||
|
return File(projectFilePath).toPath()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun doCreateAndOpenProject(projectFile: Path): Project {
|
||||||
|
return loadProjectCompat(projectFile)
|
||||||
|
}
|
||||||
|
|
||||||
|
private val projectName: String
|
||||||
|
get() {
|
||||||
|
val testName = getTestName(true)
|
||||||
|
return if (testName.contains("_")) {
|
||||||
|
testName.substring(0, testName.indexOf("_"))
|
||||||
|
} else
|
||||||
|
testName
|
||||||
|
}
|
||||||
|
|
||||||
|
protected val projectRoot: String
|
||||||
|
get() = BASE_PATH + projectName
|
||||||
|
|
||||||
|
override fun setUpModule() {}
|
||||||
|
|
||||||
|
private fun assertNoFilesInDefaultPaths() {
|
||||||
|
UsefulTestCase.assertDoesntExist(File(JAVA_CONFIGURATOR.getDefaultPathToJarFile(project)))
|
||||||
|
UsefulTestCase.assertDoesntExist(File(JS_CONFIGURATOR.getDefaultPathToJarFile(project)))
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun runTestRunnable(testRunnable: ThrowableRunnable<Throwable>) {
|
||||||
|
return runTest { super.runTestRunnable(testRunnable) }
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private val BASE_PATH = "idea/testData/configuration/"
|
||||||
|
private val TEMP_DIR_MACRO_KEY = "TEMP_TEST_DIR"
|
||||||
|
protected val JAVA_CONFIGURATOR: KotlinJavaModuleConfigurator by lazy {
|
||||||
|
object : KotlinJavaModuleConfigurator() {
|
||||||
|
override fun getDefaultPathToJarFile(project: Project) = getPathRelativeToTemp("default_jvm_lib")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected val JS_CONFIGURATOR: KotlinJsModuleConfigurator by lazy {
|
||||||
|
object : KotlinJsModuleConfigurator() {
|
||||||
|
override fun getDefaultPathToJarFile(project: Project) = getPathRelativeToTemp("default_js_lib")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun configure(
|
||||||
|
modules: List<Module>,
|
||||||
|
runtimeState: FileState,
|
||||||
|
configurator: KotlinWithLibraryConfigurator,
|
||||||
|
jarFromDist: String,
|
||||||
|
jarFromTemp: String
|
||||||
|
) {
|
||||||
|
val project = modules.first().project
|
||||||
|
val collector = createConfigureKotlinNotificationCollector(project)
|
||||||
|
|
||||||
|
val pathToJar = getPathToJar(runtimeState, jarFromDist, jarFromTemp)
|
||||||
|
for (module in modules) {
|
||||||
|
configurator.configureModule(module, pathToJar, pathToJar, collector, runtimeState)
|
||||||
|
}
|
||||||
|
collector.showNotification()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getPathToJar(runtimeState: FileState, jarFromDist: String, jarFromTemp: String) = when (runtimeState) {
|
||||||
|
FileState.EXISTS -> jarFromDist
|
||||||
|
FileState.COPY -> jarFromTemp
|
||||||
|
FileState.DO_NOT_COPY -> jarFromDist
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private val pathToExistentRuntimeJar: String
|
||||||
|
get() = PathUtil.kotlinPathsForDistDirectory.stdlibPath.parent
|
||||||
|
|
||||||
|
private val pathToExistentJsJar: String
|
||||||
|
get() = PathUtil.kotlinPathsForDistDirectory.jsStdLibJarPath.parent
|
||||||
|
|
||||||
|
protected fun assertNotConfigured(module: Module, configurator: KotlinWithLibraryConfigurator) {
|
||||||
|
TestCase.assertFalse(
|
||||||
|
String.format("Module %s should not be configured as %s Module", module.name, configurator.presentableText),
|
||||||
|
configurator.isConfigured(module)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
protected fun assertConfigured(module: Module, configurator: KotlinWithLibraryConfigurator) {
|
||||||
|
TestCase.assertTrue(
|
||||||
|
String.format(
|
||||||
|
"Module %s should be configured with configurator '%s'", module.name,
|
||||||
|
configurator.presentableText
|
||||||
|
),
|
||||||
|
configurator.isConfigured(module)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
protected fun assertProperlyConfigured(module: Module, configurator: KotlinWithLibraryConfigurator) {
|
||||||
|
assertConfigured(module, configurator)
|
||||||
|
assertNotConfigured(module, getOppositeConfigurator(configurator))
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getOppositeConfigurator(configurator: KotlinWithLibraryConfigurator): KotlinWithLibraryConfigurator {
|
||||||
|
if (configurator === JAVA_CONFIGURATOR) return JS_CONFIGURATOR
|
||||||
|
if (configurator === JS_CONFIGURATOR) return JAVA_CONFIGURATOR
|
||||||
|
|
||||||
|
throw IllegalArgumentException("Only JS_CONFIGURATOR and JAVA_CONFIGURATOR are supported")
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getPathRelativeToTemp(relativePath: String): String {
|
||||||
|
val tempPath = PathMacros.getInstance().getValue(TEMP_DIR_MACRO_KEY)
|
||||||
|
return tempPath + '/' + relativePath
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected fun configure(module: Module, jarState: FileState, configurator: KotlinProjectConfigurator) {
|
||||||
|
if (configurator is KotlinJavaModuleConfigurator) {
|
||||||
|
configure(
|
||||||
|
listOf(module), jarState,
|
||||||
|
configurator as KotlinWithLibraryConfigurator,
|
||||||
|
pathToExistentRuntimeJar, pathToNonexistentRuntimeJar
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if (configurator is KotlinJsModuleConfigurator) {
|
||||||
|
configure(
|
||||||
|
listOf(module), jarState,
|
||||||
|
configurator as KotlinWithLibraryConfigurator,
|
||||||
|
pathToExistentJsJar, pathToNonexistentJsJar
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private val pathToNonexistentRuntimeJar: String
|
||||||
|
get() {
|
||||||
|
val pathToTempKotlinRuntimeJar = FileUtil.getTempDirectory() + "/" + PathUtil.KOTLIN_JAVA_STDLIB_JAR
|
||||||
|
myFilesToDelete.add(Paths.get(pathToTempKotlinRuntimeJar))
|
||||||
|
return pathToTempKotlinRuntimeJar
|
||||||
|
}
|
||||||
|
|
||||||
|
private val pathToNonexistentJsJar: String
|
||||||
|
get() {
|
||||||
|
val pathToTempKotlinRuntimeJar = FileUtil.getTempDirectory() + "/" + PathUtil.JS_LIB_JAR_NAME
|
||||||
|
myFilesToDelete.add(Paths.get(pathToTempKotlinRuntimeJar))
|
||||||
|
return pathToTempKotlinRuntimeJar
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getTestProjectJdk(): Sdk {
|
||||||
|
val projectRootManager = ProjectRootManager.getInstance(project)
|
||||||
|
return projectRootManager.projectSdk ?: throw IllegalStateException("SDK ${projectRootManager.projectSdkName} was not found")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||||
|
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.idea.editor.quickDoc
|
||||||
|
|
||||||
|
import com.intellij.codeInsight.CodeInsightTestCase
|
||||||
|
import com.intellij.ide.hierarchy.HierarchyBrowserBaseEx
|
||||||
|
import com.intellij.ide.hierarchy.LanguageTypeHierarchy
|
||||||
|
import com.intellij.ide.hierarchy.actions.BrowseHierarchyActionBase
|
||||||
|
import com.intellij.ide.hierarchy.type.TypeHierarchyNodeDescriptor
|
||||||
|
import com.intellij.ide.hierarchy.type.TypeHierarchyTreeStructure
|
||||||
|
import com.intellij.openapi.actionSystem.CommonDataKeys
|
||||||
|
import com.intellij.openapi.editor.Editor
|
||||||
|
import com.intellij.openapi.project.Project
|
||||||
|
import com.intellij.psi.PsiClass
|
||||||
|
import com.intellij.psi.PsiElement
|
||||||
|
import com.intellij.testFramework.MapDataContext
|
||||||
|
import junit.framework.TestCase
|
||||||
|
import org.jetbrains.kotlin.idea.KotlinDocumentationProvider
|
||||||
|
import org.jetbrains.kotlin.idea.test.PluginTestCaseBase
|
||||||
|
import org.jetbrains.kotlin.test.JUnit3WithIdeaConfigurationRunner
|
||||||
|
import org.junit.runner.RunWith
|
||||||
|
|
||||||
|
@RunWith(JUnit3WithIdeaConfigurationRunner::class)
|
||||||
|
class QuickDocInHierarchyTest() : CodeInsightTestCase() {
|
||||||
|
override fun getTestDataPath(): String {
|
||||||
|
return PluginTestCaseBase.getTestDataPathBase() + "/kdoc/inTypeHierarchy/"
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testSimple() {
|
||||||
|
configureByFile(getTestName(true) + ".kt")
|
||||||
|
|
||||||
|
val context = MapDataContext()
|
||||||
|
context.put<Project>(CommonDataKeys.PROJECT, project)
|
||||||
|
context.put<Editor>(CommonDataKeys.EDITOR, editor)
|
||||||
|
|
||||||
|
val provider = BrowseHierarchyActionBase.findProvider(LanguageTypeHierarchy.INSTANCE, file, file, context)!!
|
||||||
|
val hierarchyTreeStructure = TypeHierarchyTreeStructure(
|
||||||
|
project,
|
||||||
|
provider.getTarget(context) as PsiClass,
|
||||||
|
HierarchyBrowserBaseEx.SCOPE_PROJECT
|
||||||
|
)
|
||||||
|
val hierarchyNodeDescriptor = hierarchyTreeStructure.baseDescriptor as TypeHierarchyNodeDescriptor
|
||||||
|
val doc = KotlinDocumentationProvider().generateDoc(hierarchyNodeDescriptor.psiClass as PsiElement, null)!!
|
||||||
|
|
||||||
|
TestCase.assertTrue("Invalid doc\n: $doc", doc.contains("Very special class"))
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,336 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||||
|
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.idea.perf
|
||||||
|
|
||||||
|
import com.intellij.openapi.Disposable
|
||||||
|
import com.intellij.openapi.module.Module
|
||||||
|
import com.intellij.openapi.project.Project
|
||||||
|
import com.intellij.openapi.util.Disposer
|
||||||
|
import com.intellij.psi.*
|
||||||
|
import com.intellij.psi.search.GlobalSearchScope
|
||||||
|
import com.intellij.psi.util.MethodSignature
|
||||||
|
import com.intellij.testFramework.UsefulTestCase
|
||||||
|
import com.intellij.util.PairProcessor
|
||||||
|
import com.intellij.util.ref.DebugReflectionUtil
|
||||||
|
import junit.framework.TestCase
|
||||||
|
import org.jetbrains.kotlin.asJava.KotlinAsJavaSupport
|
||||||
|
import org.jetbrains.kotlin.asJava.LightClassGenerationSupport
|
||||||
|
import org.jetbrains.kotlin.asJava.classes.*
|
||||||
|
import org.jetbrains.kotlin.asJava.elements.KtLightNullabilityAnnotation
|
||||||
|
import org.jetbrains.kotlin.asJava.elements.KtLightPsiArrayInitializerMemberValue
|
||||||
|
import org.jetbrains.kotlin.config.LanguageFeature
|
||||||
|
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||||
|
import org.jetbrains.kotlin.idea.project.languageVersionSettings
|
||||||
|
import org.jetbrains.kotlin.load.kotlin.NON_EXISTENT_CLASS_NAME
|
||||||
|
import org.jetbrains.kotlin.name.FqName
|
||||||
|
import org.jetbrains.kotlin.psi.KtClassOrObject
|
||||||
|
import org.jetbrains.kotlin.psi.KtFile
|
||||||
|
import org.jetbrains.kotlin.psi.KtScript
|
||||||
|
import org.junit.Assert
|
||||||
|
import kotlin.test.assertFails
|
||||||
|
|
||||||
|
fun UsefulTestCase.forceUsingOldLightClassesForTest() {
|
||||||
|
KtUltraLightSupport.forceUsingOldLightClasses = true
|
||||||
|
Disposer.register(testRootDisposable, Disposable {
|
||||||
|
KtUltraLightSupport.forceUsingOldLightClasses = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
object UltraLightChecker {
|
||||||
|
fun checkClassEquivalence(file: KtFile) {
|
||||||
|
for (ktClass in allClasses(file)) {
|
||||||
|
checkClassEquivalence(ktClass)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun checkForReleaseCoroutine(sourceFileText: String, module: Module) {
|
||||||
|
if (sourceFileText.contains("//RELEASE_COROUTINE_NEEDED")) {
|
||||||
|
TestCase.assertTrue(
|
||||||
|
"Test should be runned under language version that supports released coroutines",
|
||||||
|
module.languageVersionSettings.supportsFeature(LanguageFeature.ReleaseCoroutines)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun allClasses(file: KtFile): List<KtClassOrObject> =
|
||||||
|
SyntaxTraverser.psiTraverser(file).filter(KtClassOrObject::class.java).toList()
|
||||||
|
|
||||||
|
fun checkFacadeEquivalence(
|
||||||
|
fqName: FqName,
|
||||||
|
searchScope: GlobalSearchScope,
|
||||||
|
project: Project
|
||||||
|
): KtLightClassForFacade? {
|
||||||
|
|
||||||
|
val oldForceFlag = KtUltraLightSupport.forceUsingOldLightClasses
|
||||||
|
KtUltraLightSupport.forceUsingOldLightClasses = true
|
||||||
|
val gold = KtLightClassForFacade.createForFacadeNoCache(fqName, searchScope, project)
|
||||||
|
KtUltraLightSupport.forceUsingOldLightClasses = false
|
||||||
|
val ultraLightClass = KtLightClassForFacade.createForFacadeNoCache(fqName, searchScope, project) ?: return null
|
||||||
|
KtUltraLightSupport.forceUsingOldLightClasses = oldForceFlag
|
||||||
|
|
||||||
|
checkClassEquivalenceByRendering(gold, ultraLightClass)
|
||||||
|
|
||||||
|
return ultraLightClass
|
||||||
|
}
|
||||||
|
|
||||||
|
fun checkClassEquivalence(ktClass: KtClassOrObject): KtUltraLightClass? {
|
||||||
|
val gold = KtLightClassForSourceDeclaration.createNoCache(ktClass, forceUsingOldLightClasses = true)
|
||||||
|
val ultraLightClass = LightClassGenerationSupport.getInstance(ktClass.project).createUltraLightClass(ktClass) ?: return null
|
||||||
|
|
||||||
|
val secondULInstance = LightClassGenerationSupport.getInstance(ktClass.project).createUltraLightClass(ktClass)
|
||||||
|
Assert.assertNotNull(secondULInstance)
|
||||||
|
Assert.assertTrue(ultraLightClass !== secondULInstance)
|
||||||
|
secondULInstance!!
|
||||||
|
Assert.assertEquals(ultraLightClass.ownMethods.size, secondULInstance.ownMethods.size)
|
||||||
|
Assert.assertTrue(ultraLightClass.ownMethods.containsAll(secondULInstance.ownMethods))
|
||||||
|
|
||||||
|
checkClassEquivalenceByRendering(gold, ultraLightClass)
|
||||||
|
|
||||||
|
return ultraLightClass
|
||||||
|
}
|
||||||
|
|
||||||
|
fun checkScriptEquivalence(script: KtScript): KtLightClass {
|
||||||
|
|
||||||
|
val ultraLightScript: KtLightClass?
|
||||||
|
|
||||||
|
val oldForceFlag = KtUltraLightSupport.forceUsingOldLightClasses
|
||||||
|
try {
|
||||||
|
KtUltraLightSupport.forceUsingOldLightClasses = false
|
||||||
|
ultraLightScript = KotlinAsJavaSupport.getInstance(script.project).getLightClassForScript(script)
|
||||||
|
TestCase.assertTrue(ultraLightScript is KtUltraLightClassForScript)
|
||||||
|
ultraLightScript!!
|
||||||
|
val gold = KtLightClassForScript.createNoCache(script, forceUsingOldLightClasses = true)
|
||||||
|
checkClassEquivalenceByRendering(gold, ultraLightScript)
|
||||||
|
} finally {
|
||||||
|
KtUltraLightSupport.forceUsingOldLightClasses = oldForceFlag
|
||||||
|
}
|
||||||
|
|
||||||
|
return ultraLightScript!!
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun checkClassEquivalenceByRendering(gold: PsiClass?, ultraLightClass: PsiClass) {
|
||||||
|
if (gold != null) {
|
||||||
|
Assert.assertFalse(gold.javaClass.name.contains("Ultra"))
|
||||||
|
}
|
||||||
|
|
||||||
|
val goldText = gold?.renderClass().orEmpty()
|
||||||
|
val ultraText = ultraLightClass.renderClass()
|
||||||
|
|
||||||
|
if (goldText != ultraText) {
|
||||||
|
Assert.assertEquals(
|
||||||
|
"// Classic implementation:\n$goldText",
|
||||||
|
"// Ultra-light implementation:\n$ultraText"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun PsiAnnotation.renderAnnotation(): String {
|
||||||
|
|
||||||
|
val renderedAttributes = parameterList.attributes.map {
|
||||||
|
val attributeValue = it.value?.renderAnnotationMemberValue() ?: "?"
|
||||||
|
|
||||||
|
val name = when {
|
||||||
|
it.name === null && qualifiedName?.startsWith("java.lang.annotation.") == true -> "value"
|
||||||
|
else -> it.name
|
||||||
|
}
|
||||||
|
|
||||||
|
if (name !== null) "$name = $attributeValue" else attributeValue
|
||||||
|
}
|
||||||
|
return "@$qualifiedName(${renderedAttributes.joinToString()})"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private fun PsiModifierListOwner.renderModifiers(typeIfApplicable: PsiType? = null): String {
|
||||||
|
val annotationsBuffer = mutableListOf<String>()
|
||||||
|
for (annotation in annotations) {
|
||||||
|
if (annotation is KtLightNullabilityAnnotation<*> && skipRenderingNullability(typeIfApplicable)) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
annotationsBuffer.add(
|
||||||
|
annotation.renderAnnotation() + (if (this is PsiParameter) " " else "\n")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
annotationsBuffer.sort()
|
||||||
|
|
||||||
|
val resultBuffer = StringBuffer(annotationsBuffer.joinToString(separator = ""))
|
||||||
|
for (modifier in PsiModifier.MODIFIERS.filter(::hasModifierProperty)) {
|
||||||
|
resultBuffer.append(modifier).append(" ")
|
||||||
|
}
|
||||||
|
return resultBuffer.toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun PsiModifierListOwner.skipRenderingNullability(typeIfApplicable: PsiType?) =
|
||||||
|
isPrimitiveOrNonExisting(typeIfApplicable) || isPrivateOrParameterInPrivateMethod()
|
||||||
|
|
||||||
|
private val NON_EXISTENT_QUALIFIED_CLASS_NAME = NON_EXISTENT_CLASS_NAME.replace("/", ".")
|
||||||
|
|
||||||
|
private fun isPrimitiveOrNonExisting(typeIfApplicable: PsiType?): Boolean {
|
||||||
|
if (typeIfApplicable is PsiPrimitiveType) return true
|
||||||
|
if (typeIfApplicable?.getCanonicalText(false) == NON_EXISTENT_QUALIFIED_CLASS_NAME) return true
|
||||||
|
|
||||||
|
return typeIfApplicable is PsiPrimitiveType
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun PsiType.renderType() = getCanonicalText(true)
|
||||||
|
|
||||||
|
private fun PsiReferenceList?.renderRefList(keyword: String, sortReferences: Boolean = true): String {
|
||||||
|
if (this == null) return ""
|
||||||
|
|
||||||
|
val references = referencedTypes
|
||||||
|
if (references.isEmpty()) return ""
|
||||||
|
|
||||||
|
val referencesTypes = references.map { it.renderType() }.toTypedArray()
|
||||||
|
|
||||||
|
if (sortReferences) referencesTypes.sort()
|
||||||
|
|
||||||
|
return " " + keyword + " " + referencesTypes.joinToString()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun PsiVariable.renderVar(): String {
|
||||||
|
var result = this.renderModifiers(type) + type.renderType() + " " + name
|
||||||
|
if (this is PsiParameter && this.isVarArgs) {
|
||||||
|
result += " /* vararg */"
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasInitializer()) {
|
||||||
|
result += " = ${initializer?.text} /* initializer type: ${initializer?.type?.renderType()} */"
|
||||||
|
}
|
||||||
|
|
||||||
|
computeConstantValue()?.let { result += " /* constant value $it */" }
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun Array<PsiTypeParameter>.renderTypeParams() =
|
||||||
|
if (isEmpty()) ""
|
||||||
|
else "<" + joinToString {
|
||||||
|
val bounds =
|
||||||
|
if (it.extendsListTypes.isNotEmpty())
|
||||||
|
" extends " + it.extendsListTypes.joinToString(" & ", transform = { it.renderType() })
|
||||||
|
else ""
|
||||||
|
it.name!! + bounds
|
||||||
|
} + "> "
|
||||||
|
|
||||||
|
private fun PsiAnnotationMemberValue.renderAnnotationMemberValue(): String = when (this) {
|
||||||
|
is KtLightPsiArrayInitializerMemberValue -> "{${initializers.joinToString { it.renderAnnotationMemberValue() }}}"
|
||||||
|
is PsiAnnotation -> renderAnnotation()
|
||||||
|
else -> text
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun PsiMethod.renderMethod() =
|
||||||
|
renderModifiers(returnType) +
|
||||||
|
(if (isVarArgs) "/* vararg */ " else "") +
|
||||||
|
typeParameters.renderTypeParams() +
|
||||||
|
(returnType?.renderType() ?: "") + " " +
|
||||||
|
name +
|
||||||
|
"(" + parameterList.parameters.joinToString { it.renderModifiers(it.type) + it.type.renderType() } + ")" +
|
||||||
|
(this as? PsiAnnotationMethod)?.defaultValue?.let { " default " + it.renderAnnotationMemberValue() }.orEmpty() +
|
||||||
|
throwsList.referencedTypes.let { thrownTypes ->
|
||||||
|
if (thrownTypes.isEmpty()) ""
|
||||||
|
else " throws " + thrownTypes.joinToString { it.renderType() }
|
||||||
|
} +
|
||||||
|
";" +
|
||||||
|
"// ${getSignature(PsiSubstitutor.EMPTY).renderSignature()}"
|
||||||
|
|
||||||
|
private fun MethodSignature.renderSignature(): String {
|
||||||
|
val typeParams = typeParameters.renderTypeParams()
|
||||||
|
val paramTypes = parameterTypes.joinToString(prefix = "(", postfix = ")") { it.renderType() }
|
||||||
|
val name = if (isConstructor) ".ctor" else name
|
||||||
|
return "$typeParams $name$paramTypes"
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun PsiEnumConstant.renderEnumConstant(): String {
|
||||||
|
val initializingClass = initializingClass ?: return name
|
||||||
|
|
||||||
|
return buildString {
|
||||||
|
appendLine("$name {")
|
||||||
|
append(initializingClass.renderMembers())
|
||||||
|
append("}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun PsiClass.renderClass(): String {
|
||||||
|
val classWord = when {
|
||||||
|
isAnnotationType -> "@interface"
|
||||||
|
isInterface -> "interface"
|
||||||
|
isEnum -> "enum"
|
||||||
|
else -> "class"
|
||||||
|
}
|
||||||
|
|
||||||
|
return buildString {
|
||||||
|
append(renderModifiers())
|
||||||
|
append("$classWord ")
|
||||||
|
append("$name /* $qualifiedName*/")
|
||||||
|
append(typeParameters.renderTypeParams())
|
||||||
|
append(extendsList.renderRefList("extends"))
|
||||||
|
append(implementsList.renderRefList("implements"))
|
||||||
|
appendLine(" {")
|
||||||
|
|
||||||
|
if (isEnum) {
|
||||||
|
append(
|
||||||
|
fields
|
||||||
|
.filterIsInstance<PsiEnumConstant>()
|
||||||
|
.joinToString(",\n") { it.renderEnumConstant() }.prependDefaultIndent()
|
||||||
|
)
|
||||||
|
append(";\n\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
append(renderMembers())
|
||||||
|
append("}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun PsiClass.renderMembers(): String {
|
||||||
|
return buildString {
|
||||||
|
appendSorted(
|
||||||
|
fields
|
||||||
|
.filterNot { it is PsiEnumConstant }
|
||||||
|
.map { it.renderVar().prependDefaultIndent() + ";\n\n" }
|
||||||
|
)
|
||||||
|
|
||||||
|
appendSorted(
|
||||||
|
methods
|
||||||
|
.map { it.renderMethod().prependDefaultIndent() + "\n\n" }
|
||||||
|
)
|
||||||
|
|
||||||
|
appendSorted(
|
||||||
|
innerClasses.map { "class ${it.name} ...\n\n".prependDefaultIndent() }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun StringBuilder.appendSorted(list: List<String>) {
|
||||||
|
append(list.sorted().joinToString(""))
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun String.prependDefaultIndent() = prependIndent(" ")
|
||||||
|
|
||||||
|
private fun checkDescriptorLeakOnElement(element: PsiElement) {
|
||||||
|
DebugReflectionUtil.walkObjects(
|
||||||
|
10,
|
||||||
|
mapOf(element to element.javaClass.name),
|
||||||
|
Any::class.java,
|
||||||
|
{ true },
|
||||||
|
PairProcessor { value, backLink ->
|
||||||
|
if (value is DeclarationDescriptor) {
|
||||||
|
assertFails {
|
||||||
|
"""Leaked descriptor ${value.javaClass.name} in ${element.javaClass.name}\n$backLink"""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fun checkDescriptorsLeak(lightClass: KtLightClass) {
|
||||||
|
checkDescriptorLeakOnElement(lightClass)
|
||||||
|
lightClass.methods.forEach {
|
||||||
|
checkDescriptorLeakOnElement(it)
|
||||||
|
it.parameterList.parameters.forEach { parameter -> checkDescriptorLeakOnElement(parameter) }
|
||||||
|
}
|
||||||
|
lightClass.fields.forEach { checkDescriptorLeakOnElement(it) }
|
||||||
|
}
|
||||||
|
}
|
||||||
+207
@@ -0,0 +1,207 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||||
|
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.idea.refactoring.suggested
|
||||||
|
|
||||||
|
import com.intellij.lang.Language
|
||||||
|
import com.intellij.openapi.actionSystem.IdeActions
|
||||||
|
import com.intellij.openapi.application.runWriteAction
|
||||||
|
import com.intellij.openapi.command.executeCommand
|
||||||
|
import com.intellij.openapi.fileTypes.FileType
|
||||||
|
import com.intellij.psi.PsiDocumentManager
|
||||||
|
import com.intellij.psi.PsiFile
|
||||||
|
import com.intellij.refactoring.suggested.BaseSuggestedRefactoringChangeCollectorTest
|
||||||
|
import com.intellij.refactoring.suggested.SuggestedRefactoringSupport.Parameter
|
||||||
|
import com.intellij.refactoring.suggested.SuggestedRefactoringSupport.Signature
|
||||||
|
import com.intellij.util.ThrowableRunnable
|
||||||
|
import org.jetbrains.kotlin.idea.KotlinFileType
|
||||||
|
import org.jetbrains.kotlin.idea.KotlinLanguage
|
||||||
|
import org.jetbrains.kotlin.psi.*
|
||||||
|
import org.jetbrains.kotlin.test.runTest
|
||||||
|
|
||||||
|
class KotlinSuggestedRefactoringChangeCollectorTest : BaseSuggestedRefactoringChangeCollectorTest<KtNamedFunction>() {
|
||||||
|
override val fileType: FileType
|
||||||
|
get() = KotlinFileType.INSTANCE
|
||||||
|
|
||||||
|
override val language: Language
|
||||||
|
get() = KotlinLanguage.INSTANCE
|
||||||
|
|
||||||
|
override fun addDeclaration(file: PsiFile, text: String): KtNamedFunction {
|
||||||
|
val psiFactory = KtPsiFactory(project)
|
||||||
|
return (file as KtFile).add(psiFactory.createDeclaration(text)) as KtNamedFunction
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun Signature.presentation(labelForParameterId: (Any) -> String?): String {
|
||||||
|
return buildString {
|
||||||
|
append("fun ")
|
||||||
|
val receiverType = (additionalData as KotlinSignatureAdditionalData?)?.receiverType
|
||||||
|
if (receiverType != null) {
|
||||||
|
append(receiverType)
|
||||||
|
append(".")
|
||||||
|
}
|
||||||
|
append(name)
|
||||||
|
append("(")
|
||||||
|
parameters.joinTo(this, separator = ", ") { it.presentation(labelForParameterId(it.id)) }
|
||||||
|
append(")")
|
||||||
|
if (type != null) {
|
||||||
|
append(": ")
|
||||||
|
append(type)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun Parameter.presentation(label: String?): String {
|
||||||
|
return buildString {
|
||||||
|
if (modifiers.isNotEmpty()) {
|
||||||
|
append(modifiers)
|
||||||
|
append(" ")
|
||||||
|
}
|
||||||
|
append(name)
|
||||||
|
append(": ")
|
||||||
|
append(type)
|
||||||
|
if (label != null) {
|
||||||
|
append(" (")
|
||||||
|
append(label)
|
||||||
|
append(")")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createType(text: String): KtTypeReference {
|
||||||
|
return KtPsiFactory(project).createType(text)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createParameter(text: String): KtParameter {
|
||||||
|
return KtPsiFactory(project).createParameter(text)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testAddParameter() {
|
||||||
|
doTest(
|
||||||
|
"fun foo(p1: Int) {}",
|
||||||
|
{ it.valueParameterList!!.addParameter(createParameter("p2: Int")) },
|
||||||
|
expectedOldSignature = "fun foo(p1: Int)",
|
||||||
|
expectedNewSignature = "fun foo(p1: Int (initialIndex = 0), p2: Int (new))"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testRemoveParameter() {
|
||||||
|
doTest(
|
||||||
|
"fun foo(p1: Int, p2: Int) {}",
|
||||||
|
{ it.valueParameterList!!.removeParameter(0) },
|
||||||
|
expectedOldSignature = "fun foo(p1: Int, p2: Int)",
|
||||||
|
expectedNewSignature = "fun foo(p2: Int (initialIndex = 1))"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testChangeParameterType() {
|
||||||
|
doTest(
|
||||||
|
"fun foo(p1: Int, p2: Int) {}",
|
||||||
|
{ it.valueParameters[1].typeReference = createType("Any?") },
|
||||||
|
expectedOldSignature = "fun foo(p1: Int, p2: Int)",
|
||||||
|
expectedNewSignature = "fun foo(p1: Int (initialIndex = 0), p2: Any? (initialIndex = 1))"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testChangeParameterNames() {
|
||||||
|
doTest(
|
||||||
|
"fun foo(p1: Int, p2: Int) {}",
|
||||||
|
{ it.valueParameters[0].setName("newP1") },
|
||||||
|
{ it.valueParameters[1].setName("newP2") },
|
||||||
|
expectedOldSignature = "fun foo(p1: Int, p2: Int)",
|
||||||
|
expectedNewSignature = "fun foo(newP1: Int (initialIndex = 0), newP2: Int (initialIndex = 1))"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testReplaceParameter() {
|
||||||
|
doTest(
|
||||||
|
"fun foo(p1: Int, p2: Int) {}",
|
||||||
|
{ it.valueParameters[0].replace(createParameter("newP1: Long")) },
|
||||||
|
expectedOldSignature = "fun foo(p1: Int, p2: Int)",
|
||||||
|
expectedNewSignature = "fun foo(newP1: Long (new), p2: Int (initialIndex = 1))"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testReorderParametersChangeTypesAndNames() {
|
||||||
|
doTest(
|
||||||
|
"fun foo(p1: Int, p2: Int, p3: Int) {}",
|
||||||
|
{
|
||||||
|
editor.caretModel.moveToOffset(it.valueParameters[2].textOffset)
|
||||||
|
myFixture.performEditorAction(IdeActions.MOVE_ELEMENT_LEFT)
|
||||||
|
myFixture.performEditorAction(IdeActions.MOVE_ELEMENT_LEFT)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
executeCommand {
|
||||||
|
runWriteAction {
|
||||||
|
it.valueParameters[0].typeReference = createType("Any?")
|
||||||
|
it.valueParameters[1].typeReference = createType("Long")
|
||||||
|
it.valueParameters[2].typeReference = createType("Double")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
executeCommand {
|
||||||
|
runWriteAction {
|
||||||
|
it.valueParameters[1].setName("newName")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
wrapIntoCommandAndWriteAction = false,
|
||||||
|
expectedOldSignature = "fun foo(p1: Int, p2: Int, p3: Int)",
|
||||||
|
expectedNewSignature = "fun foo(p3: Any? (initialIndex = 2), newName: Long (initialIndex = 0), p2: Double (initialIndex = 1))"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testReorderParametersByCutPaste() {
|
||||||
|
doTest(
|
||||||
|
"fun foo(p1: Int, p2: String, p3: Char)",
|
||||||
|
{
|
||||||
|
val offset = it.valueParameters[1].textRange.endOffset
|
||||||
|
editor.caretModel.moveToOffset(offset)
|
||||||
|
editor.selectionModel.setSelection(offset, offset + ", p3: Char".length)
|
||||||
|
myFixture.performEditorAction(IdeActions.ACTION_EDITOR_CUT)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
val offset = it.valueParameters[0].textRange.endOffset
|
||||||
|
editor.caretModel.moveToOffset(offset)
|
||||||
|
myFixture.performEditorAction(IdeActions.ACTION_EDITOR_PASTE)
|
||||||
|
},
|
||||||
|
wrapIntoCommandAndWriteAction = false,
|
||||||
|
expectedOldSignature = "fun foo(p1: Int, p2: String, p3: Char)",
|
||||||
|
expectedNewSignature = "fun foo(p1: Int (initialIndex = 0), p3: Char (initialIndex = 2), p2: String (initialIndex = 1))"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testReorderParametersByCutPasteAfterChangingName() {
|
||||||
|
doTest(
|
||||||
|
"fun foo(p1: Int, p2: String, p3: Char)",
|
||||||
|
{
|
||||||
|
executeCommand {
|
||||||
|
runWriteAction {
|
||||||
|
it.valueParameters[2].setName("p3New")
|
||||||
|
PsiDocumentManager.getInstance(project).doPostponedOperationsAndUnblockDocument(editor.document)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
val offset = it.valueParameters[1].textRange.endOffset
|
||||||
|
editor.caretModel.moveToOffset(offset)
|
||||||
|
editor.selectionModel.setSelection(offset, offset + ", p3New: Char".length)
|
||||||
|
myFixture.performEditorAction(IdeActions.ACTION_EDITOR_CUT)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
val offset = it.valueParameters[0].textRange.endOffset
|
||||||
|
editor.caretModel.moveToOffset(offset)
|
||||||
|
myFixture.performEditorAction(IdeActions.ACTION_EDITOR_PASTE)
|
||||||
|
},
|
||||||
|
wrapIntoCommandAndWriteAction = false,
|
||||||
|
expectedOldSignature = "fun foo(p1: Int, p2: String, p3: Char)",
|
||||||
|
expectedNewSignature = "fun foo(p1: Int (initialIndex = 0), p3New: Char (initialIndex = 2), p2: String (initialIndex = 1))"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun runTestRunnable(testRunnable: ThrowableRunnable<Throwable>) {
|
||||||
|
runTest { super.runTestRunnable(testRunnable) }
|
||||||
|
}
|
||||||
|
}
|
||||||
+401
@@ -0,0 +1,401 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||||
|
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.idea.refactoring.suggested
|
||||||
|
|
||||||
|
import com.intellij.openapi.actionSystem.IdeActions
|
||||||
|
import com.intellij.openapi.application.runWriteAction
|
||||||
|
import com.intellij.openapi.command.executeCommand
|
||||||
|
import com.intellij.openapi.fileTypes.FileType
|
||||||
|
import com.intellij.refactoring.suggested.BaseSuggestedRefactoringChangeListenerTest
|
||||||
|
import com.intellij.util.ThrowableRunnable
|
||||||
|
import org.jetbrains.kotlin.idea.KotlinFileType
|
||||||
|
import org.jetbrains.kotlin.psi.*
|
||||||
|
import org.jetbrains.kotlin.psi.psiUtil.endOffset
|
||||||
|
import org.jetbrains.kotlin.psi.psiUtil.findDescendantOfType
|
||||||
|
import org.jetbrains.kotlin.psi.psiUtil.startOffset
|
||||||
|
import org.jetbrains.kotlin.resolve.ImportPath
|
||||||
|
import org.jetbrains.kotlin.test.runTest
|
||||||
|
|
||||||
|
class KotlinSuggestedRefactoringChangeListenerTest : BaseSuggestedRefactoringChangeListenerTest() {
|
||||||
|
override val fileType: FileType
|
||||||
|
get() = KotlinFileType.INSTANCE
|
||||||
|
|
||||||
|
fun test1() {
|
||||||
|
setup("fun foo(<caret>) {}")
|
||||||
|
|
||||||
|
perform("editingStarted: 'foo()'") { myFixture.type("p") }
|
||||||
|
|
||||||
|
perform("nextSignature: 'foo(p)'") { commitAll() }
|
||||||
|
perform { myFixture.type(":") }
|
||||||
|
perform { myFixture.type(" S") }
|
||||||
|
perform { myFixture.type("tr") }
|
||||||
|
perform("nextSignature: 'foo(p: Str)'") { commitAll() }
|
||||||
|
perform { myFixture.type("ing") }
|
||||||
|
perform("nextSignature: 'foo(p: String)'") { commitAll() }
|
||||||
|
|
||||||
|
perform {
|
||||||
|
perform { myFixture.type(", ") }
|
||||||
|
commitAll()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testCompletion() {
|
||||||
|
setup("fun foo(<caret>) {}")
|
||||||
|
|
||||||
|
perform("editingStarted: 'foo()'") { myFixture.type("p: DoubleArra") }
|
||||||
|
perform("nextSignature: 'foo(p: DoubleArra)'", "nextSignature: 'foo(p: DoubleArray)'") { myFixture.completeBasic() }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testChangeOutsideSignature() {
|
||||||
|
setup("fun foo(<caret>) {}")
|
||||||
|
|
||||||
|
perform("editingStarted: 'foo()'") { myFixture.type("p: A") }
|
||||||
|
perform("reset") {
|
||||||
|
insertString(editor.document.textLength, "\nval")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testEditOtherSignature() {
|
||||||
|
setup("fun foo(<caret>) {}\nfun bar() = 0")
|
||||||
|
|
||||||
|
val otherFunction = (file as KtFile).declarations[1] as KtNamedFunction
|
||||||
|
val offset = otherFunction.valueParameterList!!.startOffset + 1
|
||||||
|
val marker = editor.document.createRangeMarker(offset, offset)
|
||||||
|
|
||||||
|
perform("editingStarted: 'foo()'") { myFixture.type("p: A") }
|
||||||
|
perform("nextSignature: 'foo(p: A)'") { commitAll() }
|
||||||
|
|
||||||
|
perform("reset", "editingStarted: 'bar()'", "nextSignature: 'bar(p1: String)'") {
|
||||||
|
assert(marker.isValid)
|
||||||
|
insertString(marker.startOffset, "p1: String")
|
||||||
|
commitAll()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testChangeInAnotherFile() {
|
||||||
|
setup("fun foo(<caret>) {}")
|
||||||
|
|
||||||
|
perform("editingStarted: 'foo()'") { myFixture.type("p: A") }
|
||||||
|
perform("reset") {
|
||||||
|
setup("")
|
||||||
|
myFixture.type(" ")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testAddImport() {
|
||||||
|
setup("fun foo(<caret>) {}")
|
||||||
|
|
||||||
|
perform("editingStarted: 'foo()'", "nextSignature: 'foo(p: Any)'") {
|
||||||
|
myFixture.type("p: Any")
|
||||||
|
commitAll()
|
||||||
|
}
|
||||||
|
perform("nextSignature: 'foo(p: Any)'", "nextSignature: 'foo(p: Any)'") {
|
||||||
|
addImport("java.util.ArrayList")
|
||||||
|
}
|
||||||
|
perform("nextSignature: 'foo(p: Any, p2: String)'") {
|
||||||
|
myFixture.type(", p2: String")
|
||||||
|
commitAll()
|
||||||
|
}
|
||||||
|
perform("nextSignature: 'foo(p: Any, p2: String)'", "nextSignature: 'foo(p: Any, p2: String)'") {
|
||||||
|
addImport("java.util.Date")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testAddImportWithBlankLineInsertion() {
|
||||||
|
setup(
|
||||||
|
"""
|
||||||
|
import foo.bar
|
||||||
|
fun foo(<caret>) {}
|
||||||
|
""".trimIndent()
|
||||||
|
)
|
||||||
|
|
||||||
|
perform("editingStarted: 'foo()'", "nextSignature: 'foo(p: ArrayList)'") {
|
||||||
|
myFixture.type("p: ArrayList")
|
||||||
|
commitAll()
|
||||||
|
}
|
||||||
|
perform("nextSignature: 'foo(p: ArrayList)'", "nextSignature: 'foo(p: ArrayList)'") {
|
||||||
|
addImport("java.util.ArrayList")
|
||||||
|
}
|
||||||
|
perform("nextSignature: 'foo(p: ArrayList<String>)'") {
|
||||||
|
myFixture.type("<String>")
|
||||||
|
commitAll()
|
||||||
|
}
|
||||||
|
perform("nextSignature: 'foo(p: ArrayList<String>, p2: Any)'") {
|
||||||
|
myFixture.type(", p2: Any")
|
||||||
|
commitAll()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testAddImportWithBlankLinesRemoval() {
|
||||||
|
setup(
|
||||||
|
"""
|
||||||
|
import foo.bar
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
fun foo(<caret>) {}
|
||||||
|
""".trimIndent()
|
||||||
|
)
|
||||||
|
|
||||||
|
perform("editingStarted: 'foo()'", "nextSignature: 'foo(p: ArrayList)'") {
|
||||||
|
myFixture.type("p: ArrayList")
|
||||||
|
commitAll()
|
||||||
|
}
|
||||||
|
perform("nextSignature: 'foo(p: ArrayList)'", "nextSignature: 'foo(p: ArrayList)'") {
|
||||||
|
addImport("java.util.ArrayList")
|
||||||
|
}
|
||||||
|
perform("nextSignature: 'foo(p: ArrayList<String>)'") {
|
||||||
|
myFixture.type("<String>")
|
||||||
|
commitAll()
|
||||||
|
}
|
||||||
|
perform("nextSignature: 'foo(p: ArrayList<String>, p2: Any)'") {
|
||||||
|
myFixture.type(", p2: Any")
|
||||||
|
commitAll()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testReorderParameters() {
|
||||||
|
setup("fun foo(p1: String, p2: Any, p3<caret>: Int) {}")
|
||||||
|
|
||||||
|
perform("editingStarted: 'foo(p1: String, p2: Any, p3: Int)'") {
|
||||||
|
myFixture.performEditorAction(IdeActions.MOVE_ELEMENT_LEFT)
|
||||||
|
}
|
||||||
|
perform("nextSignature: 'foo(p1: String, p3: Int, p2: Any)'") {
|
||||||
|
myFixture.performEditorAction(IdeActions.MOVE_ELEMENT_LEFT)
|
||||||
|
}
|
||||||
|
perform("nextSignature: 'foo(p3: Int, p1: String, p2: Any)'") {
|
||||||
|
commitAll()
|
||||||
|
}
|
||||||
|
perform("nextSignature: 'foo(p1: String, p3: Int, p2: Any)'") {
|
||||||
|
myFixture.performEditorAction(IdeActions.MOVE_ELEMENT_RIGHT)
|
||||||
|
commitAll()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testAddParameterViaPsi() {
|
||||||
|
setup("fun foo(p1: Int) {}")
|
||||||
|
|
||||||
|
val function = (file as KtFile).declarations.single() as KtFunction
|
||||||
|
perform(
|
||||||
|
"editingStarted: 'foo(p1: Int)'",
|
||||||
|
"nextSignature: 'foo(p1: Int,)'",
|
||||||
|
"nextSignature: 'foo(p1: Int,p2: Int)'",
|
||||||
|
"nextSignature: 'foo(p1: Int, p2: Int)'"
|
||||||
|
) {
|
||||||
|
executeCommand {
|
||||||
|
runWriteAction {
|
||||||
|
function.valueParameterList!!.addParameter(KtPsiFactory(project).createParameter("p2: Int"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testCommentTyping() {
|
||||||
|
setup("fun foo(<caret>) {}")
|
||||||
|
|
||||||
|
perform("editingStarted: 'foo()'", "nextSignature: 'foo(p1: Any)'") {
|
||||||
|
myFixture.type("p1: Any")
|
||||||
|
commitAll()
|
||||||
|
}
|
||||||
|
|
||||||
|
perform {
|
||||||
|
myFixture.type("/*")
|
||||||
|
commitAll()
|
||||||
|
}
|
||||||
|
|
||||||
|
perform {
|
||||||
|
myFixture.type(" this is comment for parameter")
|
||||||
|
commitAll()
|
||||||
|
}
|
||||||
|
|
||||||
|
perform("nextSignature: 'foo(p1: Any/* this is comment for parameter*/)'") {
|
||||||
|
myFixture.type("*/")
|
||||||
|
commitAll()
|
||||||
|
}
|
||||||
|
|
||||||
|
perform {
|
||||||
|
myFixture.type(", p2: Int /*")
|
||||||
|
commitAll()
|
||||||
|
}
|
||||||
|
|
||||||
|
perform {
|
||||||
|
myFixture.type("this is comment for another parameter")
|
||||||
|
commitAll()
|
||||||
|
}
|
||||||
|
|
||||||
|
perform("nextSignature: 'foo(p1: Any/* this is comment for parameter*/, p2: Int /*this is comment for another parameter*/)'") {
|
||||||
|
myFixture.type("*/")
|
||||||
|
commitAll()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testAddReturnType() {
|
||||||
|
setup(
|
||||||
|
"""
|
||||||
|
interface I {
|
||||||
|
fun foo()<caret>
|
||||||
|
}
|
||||||
|
""".trimIndent()
|
||||||
|
)
|
||||||
|
|
||||||
|
perform("editingStarted: 'foo()'") { myFixture.type(": String") }
|
||||||
|
|
||||||
|
perform("nextSignature: 'foo(): String'") { commitAll() }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testNewLocal() {
|
||||||
|
setup(
|
||||||
|
"""
|
||||||
|
fun foo() {
|
||||||
|
<caret>
|
||||||
|
print(a)
|
||||||
|
}
|
||||||
|
""".trimIndent()
|
||||||
|
)
|
||||||
|
|
||||||
|
perform {
|
||||||
|
myFixture.type("val a")
|
||||||
|
commitAll()
|
||||||
|
myFixture.type("bcd")
|
||||||
|
commitAll()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testNewFunction() {
|
||||||
|
setup(
|
||||||
|
"""
|
||||||
|
interface I {
|
||||||
|
<caret>
|
||||||
|
}
|
||||||
|
""".trimIndent()
|
||||||
|
)
|
||||||
|
|
||||||
|
perform {
|
||||||
|
myFixture.type("fun foo_bar123(_p1: Int)")
|
||||||
|
commitAll()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testNewProperty() {
|
||||||
|
setup(
|
||||||
|
"""
|
||||||
|
interface I {
|
||||||
|
<caret>
|
||||||
|
}
|
||||||
|
""".trimIndent()
|
||||||
|
)
|
||||||
|
|
||||||
|
perform {
|
||||||
|
myFixture.type("val prop: I")
|
||||||
|
commitAll()
|
||||||
|
|
||||||
|
myFixture.type("nt")
|
||||||
|
commitAll()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testNewLocalWithNewUsage() {
|
||||||
|
setup(
|
||||||
|
"""
|
||||||
|
fun foo() {
|
||||||
|
<caret>
|
||||||
|
}
|
||||||
|
""".trimIndent()
|
||||||
|
)
|
||||||
|
|
||||||
|
perform {
|
||||||
|
myFixture.type("val a = 10")
|
||||||
|
myFixture.performEditorAction(IdeActions.ACTION_EDITOR_ENTER)
|
||||||
|
myFixture.type("print(a)")
|
||||||
|
commitAll()
|
||||||
|
}
|
||||||
|
|
||||||
|
perform("editingStarted: 'a'", "nextSignature: 'abcd'") {
|
||||||
|
val variable = file.findDescendantOfType<KtProperty>()!!
|
||||||
|
myFixture.editor.caretModel.moveToOffset(variable.nameIdentifier!!.endOffset)
|
||||||
|
myFixture.type("bcd")
|
||||||
|
commitAll()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testNewLocalBeforeExpression() {
|
||||||
|
setup(
|
||||||
|
"""
|
||||||
|
fun foo(p: Int) {
|
||||||
|
<caret>p * p
|
||||||
|
}
|
||||||
|
""".trimIndent()
|
||||||
|
)
|
||||||
|
|
||||||
|
perform {
|
||||||
|
myFixture.type("val a")
|
||||||
|
commitAll()
|
||||||
|
}
|
||||||
|
perform {
|
||||||
|
myFixture.type("bcd = ")
|
||||||
|
commitAll()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testNewClassWithConstructor() {
|
||||||
|
setup("")
|
||||||
|
|
||||||
|
perform {
|
||||||
|
myFixture.type("class C")
|
||||||
|
commitAll()
|
||||||
|
}
|
||||||
|
perform {
|
||||||
|
myFixture.type("(p: Int)")
|
||||||
|
commitAll()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testNewSecondaryConstructor() {
|
||||||
|
setup(
|
||||||
|
"""
|
||||||
|
class C {
|
||||||
|
<caret>
|
||||||
|
}
|
||||||
|
""".trimIndent()
|
||||||
|
)
|
||||||
|
|
||||||
|
perform {
|
||||||
|
myFixture.type("constructor(p1: Int)")
|
||||||
|
commitAll()
|
||||||
|
}
|
||||||
|
perform {
|
||||||
|
myFixture.type("(, p2: String)")
|
||||||
|
commitAll()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testRenameComponentVar() {
|
||||||
|
setup(
|
||||||
|
"""
|
||||||
|
fun f() {
|
||||||
|
val (<caret>a, b) = f()
|
||||||
|
}
|
||||||
|
""".trimIndent()
|
||||||
|
)
|
||||||
|
|
||||||
|
perform("editingStarted: 'a'", "nextSignature: 'newa'") {
|
||||||
|
myFixture.type("new")
|
||||||
|
commitAll()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun runTestRunnable(testRunnable: ThrowableRunnable<Throwable>) {
|
||||||
|
runTest { super.runTestRunnable(testRunnable) }
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun addImport(fqName: String) {
|
||||||
|
executeCommand {
|
||||||
|
runWriteAction {
|
||||||
|
(file as KtFile).importList!!.add(KtPsiFactory(project).createImportDirective(ImportPath.fromString(fqName)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+1589
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,373 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||||
|
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.idea.script
|
||||||
|
|
||||||
|
import com.intellij.openapi.module.JavaModuleType
|
||||||
|
import com.intellij.openapi.module.Module
|
||||||
|
import com.intellij.openapi.projectRoots.Sdk
|
||||||
|
import com.intellij.openapi.roots.ModuleRootModificationUtil
|
||||||
|
import com.intellij.openapi.roots.ProjectRootManager
|
||||||
|
import com.intellij.openapi.util.io.FileUtil
|
||||||
|
import com.intellij.openapi.util.text.StringUtil
|
||||||
|
import com.intellij.openapi.vfs.LocalFileSystem
|
||||||
|
import com.intellij.openapi.vfs.VfsUtil
|
||||||
|
import com.intellij.openapi.vfs.VirtualFile
|
||||||
|
import com.intellij.testFramework.PlatformTestCase
|
||||||
|
import com.intellij.testFramework.PsiTestUtil
|
||||||
|
import com.intellij.util.ui.UIUtil
|
||||||
|
import org.jdom.Element
|
||||||
|
import org.jetbrains.kotlin.codegen.forTestCompile.ForTestCompileRuntime
|
||||||
|
import org.jetbrains.kotlin.idea.completion.test.KotlinCompletionTestCase
|
||||||
|
import org.jetbrains.kotlin.idea.core.script.IdeScriptReportSink
|
||||||
|
import org.jetbrains.kotlin.idea.core.script.ScriptConfigurationManager.Companion.updateScriptDependenciesSynchronously
|
||||||
|
import org.jetbrains.kotlin.idea.core.script.ScriptDefinitionContributor
|
||||||
|
import org.jetbrains.kotlin.idea.core.script.ScriptDefinitionsManager
|
||||||
|
import org.jetbrains.kotlin.idea.core.script.settings.KotlinScriptingSettings
|
||||||
|
import org.jetbrains.kotlin.idea.highlighter.KotlinHighlightingUtil
|
||||||
|
import org.jetbrains.kotlin.idea.script.AbstractScriptConfigurationTest.Companion.useDefaultTemplate
|
||||||
|
import org.jetbrains.kotlin.idea.test.PluginTestCaseBase
|
||||||
|
import org.jetbrains.kotlin.idea.util.application.runWriteAction
|
||||||
|
import org.jetbrains.kotlin.idea.util.projectStructure.getModuleDir
|
||||||
|
import org.jetbrains.kotlin.psi.KtFile
|
||||||
|
import org.jetbrains.kotlin.scripting.definitions.findScriptDefinition
|
||||||
|
import org.jetbrains.kotlin.test.KotlinTestUtils
|
||||||
|
import org.jetbrains.kotlin.test.MockLibraryUtil
|
||||||
|
import org.jetbrains.kotlin.test.TestJdkKind
|
||||||
|
import org.jetbrains.kotlin.test.TestMetadata
|
||||||
|
import org.jetbrains.kotlin.test.util.addDependency
|
||||||
|
import org.jetbrains.kotlin.test.util.projectLibrary
|
||||||
|
import org.jetbrains.kotlin.utils.PathUtil
|
||||||
|
import org.jetbrains.kotlin.utils.PathUtil.KOTLIN_JAVA_SCRIPT_RUNTIME_JAR
|
||||||
|
import org.jetbrains.kotlin.utils.PathUtil.KOTLIN_SCRIPTING_COMMON_JAR
|
||||||
|
import org.jetbrains.kotlin.utils.PathUtil.KOTLIN_SCRIPTING_JVM_JAR
|
||||||
|
import java.io.File
|
||||||
|
import java.util.regex.Pattern
|
||||||
|
import kotlin.reflect.full.findAnnotation
|
||||||
|
import kotlin.script.dependencies.Environment
|
||||||
|
import kotlin.script.experimental.api.ScriptDiagnostic
|
||||||
|
|
||||||
|
// some bugs can only be reproduced when some module and script have intersecting library dependencies
|
||||||
|
private const val configureConflictingModule = "// CONFLICTING_MODULE"
|
||||||
|
|
||||||
|
private fun String.splitOrEmpty(delimeters: String) = split(delimeters).takeIf { it.size > 1 } ?: emptyList()
|
||||||
|
internal val switches = listOf(
|
||||||
|
useDefaultTemplate,
|
||||||
|
configureConflictingModule
|
||||||
|
)
|
||||||
|
|
||||||
|
abstract class AbstractScriptConfigurationTest : KotlinCompletionTestCase() {
|
||||||
|
companion object {
|
||||||
|
private const val SCRIPT_NAME = "script.kts"
|
||||||
|
|
||||||
|
val validKeys = setOf("javaHome", "sources", "classpath", "imports", "template-classes-names")
|
||||||
|
const val useDefaultTemplate = "// DEPENDENCIES:"
|
||||||
|
const val templatesSettings = "// TEMPLATES: "
|
||||||
|
}
|
||||||
|
|
||||||
|
protected fun testDataFile(fileName: String): File = File(testDataPath, fileName)
|
||||||
|
|
||||||
|
protected fun testDataFile(): File = testDataFile(fileName())
|
||||||
|
|
||||||
|
protected fun testPath(fileName: String = fileName()): String = testDataFile(fileName).toString()
|
||||||
|
|
||||||
|
protected fun testPath(): String = testPath(fileName())
|
||||||
|
|
||||||
|
protected open fun fileName(): String = KotlinTestUtils.getTestDataFileName(this::class.java, this.name) ?: (getTestName(false) + ".kt")
|
||||||
|
|
||||||
|
override fun getTestDataPath(): String {
|
||||||
|
return this::class.findAnnotation<TestMetadata>()?.value ?: super.getTestDataPath()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setUpModule() {
|
||||||
|
// do not create default module
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun findMainScript(testDir: String): File {
|
||||||
|
val scriptFile = File(testDir).walkTopDown().find { it.name == SCRIPT_NAME }
|
||||||
|
if (scriptFile != null) return scriptFile
|
||||||
|
|
||||||
|
return File(testDir).walkTopDown().singleOrNull { it.name.contains("script") }
|
||||||
|
?: error("Couldn't find $SCRIPT_NAME file in $testDir")
|
||||||
|
}
|
||||||
|
|
||||||
|
private val sdk by lazy {
|
||||||
|
runWriteAction {
|
||||||
|
val sdk = PluginTestCaseBase.addJdk(testRootDisposable) { PluginTestCaseBase.jdk(TestJdkKind.MOCK_JDK) }
|
||||||
|
ProjectRootManager.getInstance(project).projectSdk = sdk
|
||||||
|
sdk
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected fun configureScriptFile(path: String): VirtualFile {
|
||||||
|
val mainScriptFile = findMainScript(path)
|
||||||
|
return configureScriptFile(path, mainScriptFile)
|
||||||
|
}
|
||||||
|
|
||||||
|
protected fun configureScriptFile(path: String, mainScriptFile: File): VirtualFile {
|
||||||
|
val environment = createScriptEnvironment(mainScriptFile)
|
||||||
|
registerScriptTemplateProvider(environment)
|
||||||
|
|
||||||
|
File(path, "mainModule").takeIf { it.exists() }?.let {
|
||||||
|
myModule = createTestModuleFromDir(it)
|
||||||
|
}
|
||||||
|
|
||||||
|
File(path).listFiles { file -> file.name.startsWith("module") }.filter { it.exists() }.forEach {
|
||||||
|
val newModule = createTestModuleFromDir(it)
|
||||||
|
assert(myModule != null) { "Main module should exists" }
|
||||||
|
ModuleRootModificationUtil.addDependency(myModule, newModule)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (module != null) {
|
||||||
|
module.addDependency(
|
||||||
|
projectLibrary(
|
||||||
|
"script-runtime",
|
||||||
|
classesRoot = VfsUtil.findFileByIoFile(PathUtil.kotlinPathsForDistDirectory.scriptRuntimePath, true)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
if (environment["template-classes"] != null) {
|
||||||
|
module.addDependency(
|
||||||
|
projectLibrary(
|
||||||
|
"script-template-library",
|
||||||
|
classesRoot = VfsUtil.findFileByIoFile(environment["template-classes"] as File, true)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (configureConflictingModule in environment) {
|
||||||
|
val sharedLib = VfsUtil.findFileByIoFile(environment["lib-classes"] as File, true)!!
|
||||||
|
if (module == null) {
|
||||||
|
// Force create module if it doesn't exist
|
||||||
|
myModule = createTestModuleByName("mainModule")
|
||||||
|
}
|
||||||
|
module.addDependency(projectLibrary("sharedLib", classesRoot = sharedLib))
|
||||||
|
}
|
||||||
|
|
||||||
|
if (module != null) {
|
||||||
|
ModuleRootModificationUtil.updateModel(module) { model ->
|
||||||
|
model.sdk = sdk
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return createFileAndSyncDependencies(mainScriptFile)
|
||||||
|
}
|
||||||
|
|
||||||
|
private val oldScripClasspath: String? = System.getProperty("kotlin.script.classpath")
|
||||||
|
|
||||||
|
private var settings: Element? = null
|
||||||
|
|
||||||
|
override fun setUp() {
|
||||||
|
super.setUp()
|
||||||
|
|
||||||
|
settings = KotlinScriptingSettings.getInstance(project).state
|
||||||
|
|
||||||
|
ScriptDefinitionsManager.getInstance(project).getAllDefinitions().forEach {
|
||||||
|
KotlinScriptingSettings.getInstance(project).setEnabled(it, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
setUpTestProject()
|
||||||
|
}
|
||||||
|
|
||||||
|
open fun setUpTestProject() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun tearDown() {
|
||||||
|
System.setProperty("kotlin.script.classpath", oldScripClasspath ?: "")
|
||||||
|
|
||||||
|
settings?.let {
|
||||||
|
KotlinScriptingSettings.getInstance(project).loadState(it)
|
||||||
|
}
|
||||||
|
|
||||||
|
super.tearDown()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getTestProjectJdk(): Sdk {
|
||||||
|
return PluginTestCaseBase.mockJdk()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createTestModuleByName(name: String): Module {
|
||||||
|
val newModuleDir = runWriteAction { VfsUtil.createDirectoryIfMissing(project.baseDir, name) }
|
||||||
|
val newModule = createModuleAt(name, project, JavaModuleType.getModuleType(), VfsUtil.virtualToIoFile(newModuleDir).toPath())
|
||||||
|
|
||||||
|
PsiTestUtil.addSourceContentToRoots(newModule, newModuleDir)
|
||||||
|
return newModule
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createTestModuleFromDir(dir: File): Module {
|
||||||
|
return createTestModuleByName(dir.name).apply {
|
||||||
|
PlatformTestCase.copyDirContentsTo(LocalFileSystem.getInstance().findFileByIoFile(dir)!!, moduleFile!!.parent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createScriptEnvironment(scriptFile: File): Environment {
|
||||||
|
val defaultEnvironment = defaultEnvironment(scriptFile.parent + File.separator)
|
||||||
|
val env = mutableMapOf<String, Any?>()
|
||||||
|
scriptFile.forEachLine { line ->
|
||||||
|
|
||||||
|
fun iterateKeysInLine(prefix: String) {
|
||||||
|
if (line.contains(prefix)) {
|
||||||
|
line.trim().substringAfter(prefix).split(";").forEach { entry ->
|
||||||
|
val (key, values) = entry.splitOrEmpty(":").map { it.trim() }
|
||||||
|
assert(key in validKeys) { "Unexpected key: $key" }
|
||||||
|
env[key] = values.split(",").map {
|
||||||
|
val str = it.trim()
|
||||||
|
defaultEnvironment[str] ?: str
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
iterateKeysInLine(useDefaultTemplate)
|
||||||
|
iterateKeysInLine(templatesSettings)
|
||||||
|
|
||||||
|
switches.forEach {
|
||||||
|
if (it in line) {
|
||||||
|
env[it] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (env[useDefaultTemplate] != true && env["template-classes-names"] == null) {
|
||||||
|
env["template-classes-names"] = listOf("custom.scriptDefinition.Template")
|
||||||
|
}
|
||||||
|
|
||||||
|
val jdkKind = when ((env["javaHome"] as? List<String>)?.singleOrNull()) {
|
||||||
|
"9" -> TestJdkKind.FULL_JDK_9
|
||||||
|
else -> TestJdkKind.MOCK_JDK
|
||||||
|
}
|
||||||
|
runWriteAction {
|
||||||
|
val jdk = PluginTestCaseBase.addJdk(testRootDisposable) {
|
||||||
|
PluginTestCaseBase.jdk(jdkKind)
|
||||||
|
}
|
||||||
|
env["javaHome"] = File(jdk.homePath)
|
||||||
|
}
|
||||||
|
|
||||||
|
env.putAll(defaultEnvironment)
|
||||||
|
return env
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun defaultEnvironment(path: String): Map<String, File?> {
|
||||||
|
val templateOutDir = File("${path}template").takeIf { it.isDirectory }?.let {
|
||||||
|
compileLibToDir(it, *scriptClasspath())
|
||||||
|
} ?: File("idea/testData/script/definition/defaultTemplate").takeIf { it.isDirectory }?.let {
|
||||||
|
compileLibToDir(it, *scriptClasspath())
|
||||||
|
}
|
||||||
|
|
||||||
|
if (templateOutDir != null) {
|
||||||
|
System.setProperty("kotlin.script.classpath", templateOutDir.path)
|
||||||
|
}
|
||||||
|
|
||||||
|
val libSrcDir = File("${path}lib").takeIf { it.isDirectory }
|
||||||
|
|
||||||
|
val libClasses = libSrcDir?.let { compileLibToDir(it) }
|
||||||
|
|
||||||
|
var moduleSrcDir = File("${path}depModule").takeIf { it.isDirectory }
|
||||||
|
val moduleClasses = moduleSrcDir?.let { compileLibToDir(it) }
|
||||||
|
if (moduleSrcDir != null) {
|
||||||
|
val depModule = createTestModuleFromDir(moduleSrcDir)
|
||||||
|
moduleSrcDir = File(depModule.getModuleDir())
|
||||||
|
}
|
||||||
|
|
||||||
|
return mapOf(
|
||||||
|
"runtime-classes" to ForTestCompileRuntime.runtimeJarForTests(),
|
||||||
|
"runtime-source" to File("libraries/stdlib/src"),
|
||||||
|
"lib-classes" to libClasses,
|
||||||
|
"lib-source" to libSrcDir,
|
||||||
|
"module-classes" to moduleClasses,
|
||||||
|
"module-source" to moduleSrcDir,
|
||||||
|
"template-classes" to templateOutDir
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun scriptClasspath(): Array<String> {
|
||||||
|
return with(PathUtil.kotlinPathsForDistDirectory) {
|
||||||
|
arrayOf(
|
||||||
|
File(libPath, KOTLIN_JAVA_SCRIPT_RUNTIME_JAR).path,
|
||||||
|
File(libPath, KOTLIN_SCRIPTING_COMMON_JAR).path,
|
||||||
|
File(libPath, KOTLIN_SCRIPTING_JVM_JAR).path
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected fun createFileAndSyncDependencies(scriptFile: File): VirtualFile {
|
||||||
|
var script: VirtualFile? = null
|
||||||
|
if (module != null) {
|
||||||
|
script = module.moduleFile?.parent?.findChild(scriptFile.name)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (script == null) {
|
||||||
|
val target = File(project.basePath, scriptFile.name)
|
||||||
|
scriptFile.copyTo(target)
|
||||||
|
script = VfsUtil.findFileByIoFile(target, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (script == null) error("Test file with script couldn't be found in test project")
|
||||||
|
|
||||||
|
configureByExistingFile(script)
|
||||||
|
loadScriptConfigurationSynchronously(script)
|
||||||
|
return script!!
|
||||||
|
}
|
||||||
|
|
||||||
|
protected open fun loadScriptConfigurationSynchronously(script: VirtualFile) {
|
||||||
|
updateScriptDependenciesSynchronously(myFile)
|
||||||
|
|
||||||
|
// This is needed because updateScriptDependencies invalidates psiFile that was stored in myFile field
|
||||||
|
VfsUtil.markDirtyAndRefresh(false, true, true, project.baseDir)
|
||||||
|
myFile = psiManager.findFile(script)
|
||||||
|
|
||||||
|
checkHighlighting()
|
||||||
|
}
|
||||||
|
|
||||||
|
protected fun checkHighlighting(file: KtFile = myFile as KtFile) {
|
||||||
|
val reports = IdeScriptReportSink.getReports(file)
|
||||||
|
val isFatalErrorPresent = reports.any { it.severity == ScriptDiagnostic.Severity.FATAL }
|
||||||
|
assert(isFatalErrorPresent || KotlinHighlightingUtil.shouldHighlight(file)) {
|
||||||
|
"Highlighting is switched off for ${file.virtualFile.path}\n" +
|
||||||
|
"reports=$reports\n" +
|
||||||
|
"scriptDefinition=${file.findScriptDefinition()}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun compileLibToDir(srcDir: File, vararg classpath: String): File {
|
||||||
|
//TODO: tmpDir would be enough, but there is tricky fail under AS otherwise
|
||||||
|
val outDir = KotlinTestUtils.tmpDirForReusableFolder("${getTestName(false)}${srcDir.name}Out")
|
||||||
|
|
||||||
|
val kotlinSourceFiles = FileUtil.findFilesByMask(Pattern.compile(".+\\.kt$"), srcDir)
|
||||||
|
if (kotlinSourceFiles.isNotEmpty()) {
|
||||||
|
MockLibraryUtil.compileKotlin(srcDir.path, outDir, extraClasspath = *classpath)
|
||||||
|
}
|
||||||
|
|
||||||
|
val javaSourceFiles = FileUtil.findFilesByMask(Pattern.compile(".+\\.java$"), srcDir)
|
||||||
|
if (javaSourceFiles.isNotEmpty()) {
|
||||||
|
KotlinTestUtils.compileJavaFiles(
|
||||||
|
javaSourceFiles,
|
||||||
|
listOf("-cp", StringUtil.join(listOf(*classpath, outDir), File.pathSeparator), "-d", outDir.path)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return outDir
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun registerScriptTemplateProvider(environment: Environment) {
|
||||||
|
val provider = if (environment[useDefaultTemplate] == true) {
|
||||||
|
FromTextTemplateProvider(environment)
|
||||||
|
} else {
|
||||||
|
CustomScriptTemplateProvider(environment)
|
||||||
|
}
|
||||||
|
|
||||||
|
addExtensionPointInTest(
|
||||||
|
ScriptDefinitionContributor.EP_NAME,
|
||||||
|
project,
|
||||||
|
provider,
|
||||||
|
testRootDisposable
|
||||||
|
)
|
||||||
|
|
||||||
|
ScriptDefinitionsManager.getInstance(project).reloadScriptDefinitions()
|
||||||
|
|
||||||
|
UIUtil.dispatchAllInvocationEvents()
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user