Remove 172 bunchset
This commit is contained in:
@@ -1,22 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2018 JetBrains s.r.o. 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.core.CoreApplicationEnvironment
|
||||
import com.intellij.openapi.extensions.ExtensionsArea
|
||||
import com.intellij.psi.impl.compiled.ClsCustomNavigationPolicy
|
||||
|
||||
internal object IdeaExtensionPoints {
|
||||
const val IDEA_COMPATIBLE_BUILD_NUMBER = "172.9999"
|
||||
|
||||
fun registerVersionSpecificAppExtensionPoints(area: ExtensionsArea) {
|
||||
@Suppress("DEPRECATION")
|
||||
CoreApplicationEnvironment.registerExtensionPoint(area, ClsCustomNavigationPolicy.EP_NAME, ClsCustomNavigationPolicy::class.java)
|
||||
}
|
||||
|
||||
fun registerVersionSpecificProjectExtensionPoints(area: ExtensionsArea) {
|
||||
}
|
||||
}
|
||||
@@ -1,221 +0,0 @@
|
||||
-injars '<kotlin-compiler-jar-before-shrink>'(
|
||||
!org/apache/log4j/jmx/Agent*,
|
||||
!org/apache/log4j/net/JMS*,
|
||||
!org/apache/log4j/net/SMTP*,
|
||||
!org/apache/log4j/or/jms/MessageRenderer*,
|
||||
!org/jdom/xpath/Jaxen*,
|
||||
!org/jline/builtins/ssh/**,
|
||||
!org/mozilla/javascript/xml/impl/xmlbeans/**,
|
||||
!net/sf/cglib/**,
|
||||
!META-INF/maven**,
|
||||
**.class,**.properties,**.kt,**.kotlin_*,**.jnilib,**.so,**.dll,**.txt,**.caps,
|
||||
META-INF/services/**,META-INF/native/**,META-INF/extensions/**,META-INF/MANIFEST.MF,
|
||||
messages/**)
|
||||
|
||||
-outjars '<kotlin-compiler-jar>'
|
||||
|
||||
-dontnote **
|
||||
-dontwarn com.intellij.util.ui.IsRetina*
|
||||
-dontwarn com.intellij.util.RetinaImage*
|
||||
-dontwarn apple.awt.*
|
||||
-dontwarn dk.brics.automaton.*
|
||||
-dontwarn org.fusesource.**
|
||||
-dontwarn org.imgscalr.Scalr**
|
||||
-dontwarn org.xerial.snappy.SnappyBundleActivator
|
||||
-dontwarn com.intellij.util.CompressionUtil
|
||||
-dontwarn com.intellij.util.SnappyInitializer
|
||||
-dontwarn com.intellij.util.SVGLoader
|
||||
-dontwarn com.intellij.util.SVGLoader$MyTranscoder
|
||||
-dontwarn net.sf.cglib.**
|
||||
-dontwarn org.objectweb.asm.** # this is ASM3, the old version that we do not use
|
||||
-dontwarn com.sun.jna.NativeString
|
||||
-dontwarn com.sun.jna.WString
|
||||
-dontwarn com.intellij.psi.util.PsiClassUtil
|
||||
-dontwarn org.apache.hadoop.io.compress.*
|
||||
-dontwarn com.google.j2objc.annotations.Weak
|
||||
-dontwarn org.iq80.snappy.HadoopSnappyCodec$SnappyCompressionInputStream
|
||||
-dontwarn org.iq80.snappy.HadoopSnappyCodec$SnappyCompressionOutputStream
|
||||
-dontwarn com.google.common.util.concurrent.*
|
||||
-dontwarn org.apache.xerces.dom.**
|
||||
-dontwarn org.apache.xerces.util.**
|
||||
-dontwarn org.w3c.dom.ElementTraversal
|
||||
-dontwarn javaslang.match.annotation.Unapply
|
||||
-dontwarn javaslang.match.annotation.Patterns
|
||||
-dontwarn javaslang.*
|
||||
-dontwarn com.google.errorprone.**
|
||||
-dontwarn com.google.j2objc.**
|
||||
-dontwarn javax.crypto.**
|
||||
-dontwarn java.lang.invoke.MethodHandle
|
||||
-dontwarn org.jline.builtins.Nano$Buffer
|
||||
-dontwarn net.jpountz.lz4.LZ4Factory
|
||||
-dontwarn org.jetbrains.annotations.ReadOnly
|
||||
-dontwarn org.jetbrains.annotations.Mutable
|
||||
|
||||
#-libraryjars '<rtjar>'
|
||||
#-libraryjars '<jssejar>'
|
||||
#-libraryjars '<bootstrap.runtime>'
|
||||
#-libraryjars '<bootstrap.reflect>'
|
||||
#-libraryjars '<bootstrap.script.runtime>'
|
||||
#-libraryjars '<tools.jar>'
|
||||
|
||||
-dontoptimize
|
||||
-dontobfuscate
|
||||
|
||||
-keep class org.fusesource.** { *; }
|
||||
-keep class com.sun.jna.** { *; }
|
||||
|
||||
-keep class org.jetbrains.annotations.** {
|
||||
public protected *;
|
||||
}
|
||||
|
||||
-keep class javax.inject.** {
|
||||
public protected *;
|
||||
}
|
||||
|
||||
-keep class org.jetbrains.kotlin.** {
|
||||
public protected *;
|
||||
}
|
||||
|
||||
-keep class org.jetbrains.kotlin.compiler.plugin.** {
|
||||
public protected *;
|
||||
}
|
||||
|
||||
-keep class org.jetbrains.kotlin.extensions.** {
|
||||
public protected *;
|
||||
}
|
||||
|
||||
-keep class org.jetbrains.kotlin.protobuf.** {
|
||||
public protected *;
|
||||
}
|
||||
|
||||
-keep class org.jetbrains.kotlin.container.** { *; }
|
||||
|
||||
-keep class org.jetbrains.kotlin.codegen.intrinsics.IntrinsicArrayConstructorsKt { *; }
|
||||
|
||||
-keep class org.jetbrains.org.objectweb.asm.Opcodes { *; }
|
||||
|
||||
-keep class org.jetbrains.kotlin.codegen.extensions.** {
|
||||
public protected *;
|
||||
}
|
||||
|
||||
-keepclassmembers class com.intellij.openapi.vfs.VirtualFile {
|
||||
public protected *;
|
||||
}
|
||||
|
||||
-keep class com.intellij.openapi.vfs.StandardFileSystems {
|
||||
public static *;
|
||||
}
|
||||
|
||||
# needed for jar cache cleanup in the gradle plugin and compile daemon
|
||||
-keepclassmembers class com.intellij.openapi.vfs.impl.ZipHandler {
|
||||
public static void clearFileAccessorCache();
|
||||
}
|
||||
|
||||
-keep class jet.** {
|
||||
public protected *;
|
||||
}
|
||||
|
||||
-keep class com.intellij.psi.** {
|
||||
public protected *;
|
||||
}
|
||||
|
||||
# This is needed so that the platform code which parses XML wouldn't fail, see KT-16968
|
||||
# This API is used from org.jdom.input.SAXBuilder via reflection.
|
||||
-keep class org.jdom.input.JAXPParserFactory { public ** createParser(...); }
|
||||
|
||||
# for kdoc & dokka
|
||||
-keep class com.intellij.openapi.util.TextRange { *; }
|
||||
-keep class com.intellij.lang.impl.PsiBuilderImpl* {
|
||||
public protected *;
|
||||
}
|
||||
-keep class com.intellij.openapi.util.text.StringHash { *; }
|
||||
|
||||
# for j2k
|
||||
-keep class com.intellij.codeInsight.NullableNotNullManager { public protected *; }
|
||||
|
||||
# for gradle (see KT-12549)
|
||||
-keep class com.intellij.lang.properties.charset.Native2AsciiCharsetProvider { *; }
|
||||
|
||||
# for kotlin-build-common (consider repacking compiler together with kotlin-build-common and remove this part afterwards)
|
||||
-keep class com.intellij.util.io.IOUtil { public *; }
|
||||
-keep class com.intellij.openapi.util.io.FileUtil { public *; }
|
||||
-keep class com.intellij.util.SystemProperties { public *; }
|
||||
-keep class com.intellij.util.containers.hash.LinkedHashMap { *; }
|
||||
-keep class com.intellij.util.containers.ConcurrentIntObjectMap { *; }
|
||||
-keep class com.intellij.util.containers.ComparatorUtil { *; }
|
||||
-keep class com.intellij.util.io.PersistentHashMapValueStorage { *; }
|
||||
-keep class com.intellij.util.io.PersistentHashMap { *; }
|
||||
-keep class com.intellij.util.io.BooleanDataDescriptor { *; }
|
||||
-keep class com.intellij.util.io.EnumeratorStringDescriptor { *; }
|
||||
-keep class com.intellij.util.io.ExternalIntegerKeyDescriptor { *; }
|
||||
-keep class com.intellij.util.containers.hash.EqualityPolicy { *; }
|
||||
-keep class com.intellij.util.containers.hash.EqualityPolicy.* { *; }
|
||||
-keep class com.intellij.util.containers.Interner { *; }
|
||||
-keep class gnu.trove.TIntHashSet { *; }
|
||||
-keep class gnu.trove.TIntIterator { *; }
|
||||
-keep class org.iq80.snappy.SlowMemory { *; }
|
||||
-keep class javaslang.match.PatternsProcessor { *; }
|
||||
|
||||
-keepclassmembers enum * {
|
||||
public static **[] values();
|
||||
public static ** valueOf(java.lang.String);
|
||||
}
|
||||
|
||||
-keepclassmembers class * {
|
||||
** toString();
|
||||
** hashCode();
|
||||
void start();
|
||||
void stop();
|
||||
void dispose();
|
||||
}
|
||||
|
||||
-keepclassmembers class org.jetbrains.org.objectweb.asm.Opcodes {
|
||||
*** ASM5;
|
||||
}
|
||||
|
||||
-keep class org.jetbrains.org.objectweb.asm.tree.AnnotationNode { *; }
|
||||
-keep class org.jetbrains.org.objectweb.asm.tree.ClassNode { *; }
|
||||
-keep class org.jetbrains.org.objectweb.asm.tree.LocalVariableNode { *; }
|
||||
-keep class org.jetbrains.org.objectweb.asm.tree.MethodNode { *; }
|
||||
-keep class org.jetbrains.org.objectweb.asm.tree.FieldNode { *; }
|
||||
-keep class org.jetbrains.org.objectweb.asm.tree.ParameterNode { *; }
|
||||
-keep class org.jetbrains.org.objectweb.asm.tree.TypeAnnotationNode { *; }
|
||||
|
||||
-keep class org.jetbrains.org.objectweb.asm.signature.SignatureReader { *; }
|
||||
-keep class org.jetbrains.org.objectweb.asm.signature.SignatureVisitor { *; }
|
||||
|
||||
-keep class org.jetbrains.org.objectweb.asm.Type {
|
||||
public protected *;
|
||||
}
|
||||
|
||||
-keepclassmembers class org.jetbrains.org.objectweb.asm.ClassReader {
|
||||
*** SKIP_CODE;
|
||||
*** SKIP_DEBUG;
|
||||
*** SKIP_FRAMES;
|
||||
}
|
||||
|
||||
-keepclassmembers class com.intellij.openapi.project.Project {
|
||||
** getBasePath();
|
||||
}
|
||||
|
||||
# for kotlin-android-extensions in maven
|
||||
-keep class com.intellij.openapi.module.ModuleServiceManager { public *; }
|
||||
|
||||
# for building kotlin-build-common-test
|
||||
-keep class org.jetbrains.kotlin.build.SerializationUtilsKt { *; }
|
||||
|
||||
# for tools.jar
|
||||
-keep class com.sun.tools.javac.** { *; }
|
||||
-keep class com.sun.source.** { *; }
|
||||
|
||||
# for coroutines
|
||||
-keep class kotlinx.coroutines.** { *; }
|
||||
|
||||
# for webdemo
|
||||
-keep class com.intellij.openapi.progress.ProgressManager { *; }
|
||||
|
||||
# for kapt
|
||||
-keep class com.intellij.openapi.project.Project { *; }
|
||||
|
||||
# remove when KT-18563 would be fixed
|
||||
-keep class org.jetbrains.kotlin.psi.psiUtil.PsiUtilsKt { *; }
|
||||
-48
@@ -1,48 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2016 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.test.testFramework
|
||||
|
||||
import org.jetbrains.annotations.TestOnly
|
||||
import java.lang.reflect.InvocationTargetException
|
||||
import javax.swing.SwingUtilities
|
||||
|
||||
class EdtTestUtil {
|
||||
companion object {
|
||||
@TestOnly @JvmStatic fun runInEdtAndWait(runnable: Runnable) {
|
||||
runInEdtAndWait { runnable.run() }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Test only because in production you must use Application.invokeAndWait(Runnable, ModalityState).
|
||||
// The problem is - Application logs errors, but not throws. But in tests must be thrown.
|
||||
// In any case name "runInEdtAndWait" is better than "invokeAndWait".
|
||||
@TestOnly
|
||||
fun runInEdtAndWait(runnable: () -> Unit) {
|
||||
if (SwingUtilities.isEventDispatchThread()) {
|
||||
runnable()
|
||||
}
|
||||
else {
|
||||
try {
|
||||
SwingUtilities.invokeAndWait(runnable)
|
||||
}
|
||||
catch (e: InvocationTargetException) {
|
||||
throw e.cause ?: e
|
||||
}
|
||||
}
|
||||
}
|
||||
-340
@@ -1,340 +0,0 @@
|
||||
/*
|
||||
* Copyright 2000-2016 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.test.testFramework;
|
||||
|
||||
import com.intellij.core.CoreASTFactory;
|
||||
import com.intellij.lang.*;
|
||||
import com.intellij.lang.impl.PsiBuilderFactoryImpl;
|
||||
import com.intellij.mock.MockFileDocumentManagerImpl;
|
||||
import com.intellij.openapi.Disposable;
|
||||
import com.intellij.openapi.application.PathManager;
|
||||
import com.intellij.openapi.editor.Document;
|
||||
import com.intellij.openapi.editor.EditorFactory;
|
||||
import com.intellij.openapi.extensions.ExtensionPointName;
|
||||
import com.intellij.openapi.extensions.Extensions;
|
||||
import com.intellij.openapi.fileEditor.FileDocumentManager;
|
||||
import com.intellij.openapi.fileEditor.impl.LoadTextUtil;
|
||||
import com.intellij.openapi.fileTypes.FileTypeFactory;
|
||||
import com.intellij.openapi.fileTypes.FileTypeManager;
|
||||
import com.intellij.openapi.progress.EmptyProgressIndicator;
|
||||
import com.intellij.openapi.progress.ProgressManager;
|
||||
import com.intellij.openapi.progress.impl.CoreProgressManager;
|
||||
import com.intellij.openapi.util.Disposer;
|
||||
import com.intellij.openapi.util.Key;
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.openapi.util.io.FileUtil;
|
||||
import com.intellij.openapi.vfs.CharsetToolkit;
|
||||
import com.intellij.pom.PomModel;
|
||||
import com.intellij.pom.core.impl.PomModelImpl;
|
||||
import com.intellij.pom.tree.TreeAspect;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.impl.DebugUtil;
|
||||
import com.intellij.psi.impl.PsiCachedValuesFactory;
|
||||
import com.intellij.psi.impl.PsiFileFactoryImpl;
|
||||
import com.intellij.psi.impl.source.resolve.reference.ReferenceProvidersRegistry;
|
||||
import com.intellij.psi.impl.source.resolve.reference.ReferenceProvidersRegistryImpl;
|
||||
import com.intellij.psi.impl.source.text.BlockSupportImpl;
|
||||
import com.intellij.psi.impl.source.text.DiffLog;
|
||||
import com.intellij.psi.util.CachedValuesManager;
|
||||
import com.intellij.testFramework.LightVirtualFile;
|
||||
import com.intellij.testFramework.TestDataFile;
|
||||
import com.intellij.util.CachedValuesManagerImpl;
|
||||
import com.intellij.util.Function;
|
||||
import com.intellij.util.messages.MessageBus;
|
||||
import com.intellij.util.messages.MessageBusFactory;
|
||||
import junit.framework.TestCase;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.kotlin.test.testFramework.mock.*;
|
||||
import org.picocontainer.ComponentAdapter;
|
||||
import org.picocontainer.MutablePicoContainer;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Set;
|
||||
|
||||
@SuppressWarnings("ALL")
|
||||
public abstract class KtParsingTestCase extends KtPlatformLiteFixture {
|
||||
public static final Key<Document> HARD_REF_TO_DOCUMENT_KEY = Key.create("HARD_REF_TO_DOCUMENT_KEY");
|
||||
protected String myFilePrefix = "";
|
||||
protected String myFileExt;
|
||||
protected final String myFullDataPath;
|
||||
protected PsiFile myFile;
|
||||
private MockPsiManager myPsiManager;
|
||||
private PsiFileFactoryImpl myFileFactory;
|
||||
protected Language myLanguage;
|
||||
private final ParserDefinition[] myDefinitions;
|
||||
private final boolean myLowercaseFirstLetter;
|
||||
|
||||
protected KtParsingTestCase(@NonNls @NotNull String dataPath, @NotNull String fileExt, @NotNull ParserDefinition... definitions) {
|
||||
this(dataPath, fileExt, false, definitions);
|
||||
}
|
||||
|
||||
protected KtParsingTestCase(@NonNls @NotNull String dataPath, @NotNull String fileExt, boolean lowercaseFirstLetter, @NotNull ParserDefinition... definitions) {
|
||||
myDefinitions = definitions;
|
||||
myFullDataPath = getTestDataPath() + "/" + dataPath;
|
||||
myFileExt = fileExt;
|
||||
myLowercaseFirstLetter = lowercaseFirstLetter;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
super.setUp();
|
||||
initApplication();
|
||||
ComponentAdapter component = getApplication().getPicoContainer().getComponentAdapter(ProgressManager.class.getName());
|
||||
|
||||
Extensions.registerAreaClass("IDEA_PROJECT", null);
|
||||
myProject = new MockProjectEx(getTestRootDisposable());
|
||||
myPsiManager = new MockPsiManager(myProject);
|
||||
myFileFactory = new PsiFileFactoryImpl(myPsiManager);
|
||||
MutablePicoContainer appContainer = getApplication().getPicoContainer();
|
||||
registerComponentInstance(appContainer, MessageBus.class, MessageBusFactory.newMessageBus(getApplication()));
|
||||
final MockEditorFactory editorFactory = new MockEditorFactory();
|
||||
registerComponentInstance(appContainer, EditorFactory.class, editorFactory);
|
||||
registerComponentInstance(appContainer, FileDocumentManager.class, new MockFileDocumentManagerImpl(new Function<CharSequence, Document>() {
|
||||
@Override
|
||||
public Document fun(CharSequence charSequence) {
|
||||
return editorFactory.createDocument(charSequence);
|
||||
}
|
||||
}, HARD_REF_TO_DOCUMENT_KEY));
|
||||
registerComponentInstance(appContainer, PsiDocumentManager.class, new MockPsiDocumentManager());
|
||||
registerApplicationService(PsiBuilderFactory.class, new PsiBuilderFactoryImpl());
|
||||
registerApplicationService(DefaultASTFactory.class, new CoreASTFactory());
|
||||
registerApplicationService(ReferenceProvidersRegistry.class, new ReferenceProvidersRegistryImpl());
|
||||
|
||||
registerApplicationService(ProgressManager.class, new CoreProgressManager());
|
||||
|
||||
myProject.registerService(CachedValuesManager.class, new CachedValuesManagerImpl(myProject, new PsiCachedValuesFactory(myPsiManager)));
|
||||
myProject.registerService(PsiManager.class, myPsiManager);
|
||||
|
||||
this.registerExtensionPoint(FileTypeFactory.FILE_TYPE_FACTORY_EP, FileTypeFactory.class);
|
||||
registerExtensionPoint(MetaLanguage.EP_NAME, MetaLanguage.class);
|
||||
|
||||
for (ParserDefinition definition : myDefinitions) {
|
||||
addExplicitExtension(LanguageParserDefinitions.INSTANCE, definition.getFileNodeType().getLanguage(), definition);
|
||||
}
|
||||
if (myDefinitions.length > 0) {
|
||||
configureFromParserDefinition(myDefinitions[0], myFileExt);
|
||||
}
|
||||
|
||||
// That's for reparse routines
|
||||
final PomModelImpl pomModel = new PomModelImpl(myProject);
|
||||
myProject.registerService(PomModel.class, pomModel);
|
||||
new TreeAspect(pomModel);
|
||||
}
|
||||
|
||||
public void configureFromParserDefinition(ParserDefinition definition, String extension) {
|
||||
myLanguage = definition.getFileNodeType().getLanguage();
|
||||
myFileExt = extension;
|
||||
addExplicitExtension(LanguageParserDefinitions.INSTANCE, this.myLanguage, definition);
|
||||
registerComponentInstance(
|
||||
getApplication().getPicoContainer(), FileTypeManager.class,
|
||||
new KtMockFileTypeManager(new KtMockLanguageFileType(myLanguage, myFileExt)));
|
||||
}
|
||||
|
||||
protected <T> void addExplicitExtension(final LanguageExtension<T> instance, final Language language, final T object) {
|
||||
instance.addExplicitExtension(language, object);
|
||||
Disposer.register(myProject, new Disposable() {
|
||||
@Override
|
||||
public void dispose() {
|
||||
instance.removeExplicitExtension(language, object);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected <T> void registerExtensionPoint(final ExtensionPointName<T> extensionPointName, Class<T> aClass) {
|
||||
super.registerExtensionPoint(extensionPointName, aClass);
|
||||
Disposer.register(myProject, new Disposable() {
|
||||
@Override
|
||||
public void dispose() {
|
||||
Extensions.getRootArea().unregisterExtensionPoint(extensionPointName.getName());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected <T> void registerApplicationService(final Class<T> aClass, T object) {
|
||||
getApplication().registerService(aClass, object);
|
||||
Disposer.register(myProject, new Disposable() {
|
||||
@Override
|
||||
public void dispose() {
|
||||
getApplication().getPicoContainer().unregisterComponent(aClass.getName());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public MockProjectEx getProject() {
|
||||
return myProject;
|
||||
}
|
||||
|
||||
public MockPsiManager getPsiManager() {
|
||||
return myPsiManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void tearDown() throws Exception {
|
||||
super.tearDown();
|
||||
myFile = null;
|
||||
myProject = null;
|
||||
myPsiManager = null;
|
||||
}
|
||||
|
||||
protected String getTestDataPath() {
|
||||
return PathManager.getHomePath();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public final String getTestName() {
|
||||
return getTestName(myLowercaseFirstLetter);
|
||||
}
|
||||
|
||||
protected boolean includeRanges() {
|
||||
return false;
|
||||
}
|
||||
|
||||
protected boolean skipSpaces() {
|
||||
return false;
|
||||
}
|
||||
|
||||
protected boolean checkAllPsiRoots() {
|
||||
return true;
|
||||
}
|
||||
|
||||
protected void doTest(boolean checkResult) {
|
||||
String name = getTestName();
|
||||
try {
|
||||
String text = loadFile(name + "." + myFileExt);
|
||||
myFile = createPsiFile(name, text);
|
||||
ensureParsed(myFile);
|
||||
assertEquals("light virtual file text mismatch", text, ((LightVirtualFile)myFile.getVirtualFile()).getContent().toString());
|
||||
assertEquals("virtual file text mismatch", text, LoadTextUtil.loadText(myFile.getVirtualFile()));
|
||||
assertEquals("doc text mismatch", text, myFile.getViewProvider().getDocument().getText());
|
||||
assertEquals("psi text mismatch", text, myFile.getText());
|
||||
ensureCorrectReparse(myFile);
|
||||
if (checkResult){
|
||||
checkResult(name, myFile);
|
||||
}
|
||||
else{
|
||||
toParseTreeText(myFile, skipSpaces(), includeRanges());
|
||||
}
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
protected void doTest(String suffix) throws IOException {
|
||||
String name = getTestName();
|
||||
String text = loadFile(name + "." + myFileExt);
|
||||
myFile = createPsiFile(name, text);
|
||||
ensureParsed(myFile);
|
||||
assertEquals(text, myFile.getText());
|
||||
checkResult(name + suffix, myFile);
|
||||
}
|
||||
|
||||
protected void doCodeTest(String code) throws IOException {
|
||||
String name = getTestName();
|
||||
myFile = createPsiFile("a", code);
|
||||
ensureParsed(myFile);
|
||||
assertEquals(code, myFile.getText());
|
||||
checkResult(myFilePrefix + name, myFile);
|
||||
}
|
||||
|
||||
protected PsiFile createPsiFile(String name, String text) {
|
||||
return createFile(name + "." + myFileExt, text);
|
||||
}
|
||||
|
||||
protected PsiFile createFile(@NonNls String name, String text) {
|
||||
LightVirtualFile virtualFile = new LightVirtualFile(name, myLanguage, text);
|
||||
virtualFile.setCharset(CharsetToolkit.UTF8_CHARSET);
|
||||
return createFile(virtualFile);
|
||||
}
|
||||
|
||||
protected PsiFile createFile(LightVirtualFile virtualFile) {
|
||||
return myFileFactory.trySetupPsiForFile(virtualFile, myLanguage, true, false);
|
||||
}
|
||||
|
||||
protected void checkResult(@NonNls @TestDataFile String targetDataName, final PsiFile file) throws IOException {
|
||||
doCheckResult(myFullDataPath, file, checkAllPsiRoots(), targetDataName, skipSpaces(), includeRanges());
|
||||
}
|
||||
|
||||
public static void doCheckResult(String testDataDir,
|
||||
PsiFile file,
|
||||
boolean checkAllPsiRoots,
|
||||
String targetDataName,
|
||||
boolean skipSpaces,
|
||||
boolean printRanges) throws IOException {
|
||||
FileViewProvider provider = file.getViewProvider();
|
||||
Set<Language> languages = provider.getLanguages();
|
||||
|
||||
if (!checkAllPsiRoots || languages.size() == 1) {
|
||||
doCheckResult(testDataDir, targetDataName + ".txt", toParseTreeText(file, skipSpaces, printRanges).trim());
|
||||
return;
|
||||
}
|
||||
|
||||
for (Language language : languages) {
|
||||
PsiFile root = provider.getPsi(language);
|
||||
String expectedName = targetDataName + "." + language.getID() + ".txt";
|
||||
doCheckResult(testDataDir, expectedName, toParseTreeText(root, skipSpaces, printRanges).trim());
|
||||
}
|
||||
}
|
||||
|
||||
protected void checkResult(String actual) throws IOException {
|
||||
String name = getTestName();
|
||||
doCheckResult(myFullDataPath, myFilePrefix + name + ".txt", actual);
|
||||
}
|
||||
|
||||
protected void checkResult(@TestDataFile @NonNls String targetDataName, String actual) throws IOException {
|
||||
doCheckResult(myFullDataPath, targetDataName, actual);
|
||||
}
|
||||
|
||||
public static void doCheckResult(String fullPath, String targetDataName, String actual) throws IOException {
|
||||
String expectedFileName = fullPath + File.separatorChar + targetDataName;
|
||||
KtUsefulTestCase.assertSameLinesWithFile(expectedFileName, actual);
|
||||
}
|
||||
|
||||
protected static String toParseTreeText(PsiElement file, boolean skipSpaces, boolean printRanges) {
|
||||
return DebugUtil.psiToString(file, skipSpaces, printRanges);
|
||||
}
|
||||
|
||||
protected String loadFile(@NonNls @TestDataFile String name) throws IOException {
|
||||
return loadFileDefault(myFullDataPath, name);
|
||||
}
|
||||
|
||||
public static String loadFileDefault(String dir, String name) throws IOException {
|
||||
return FileUtil.loadFile(new File(dir, name), CharsetToolkit.UTF8, true).trim();
|
||||
}
|
||||
|
||||
public static void ensureParsed(PsiFile file) {
|
||||
file.accept(new PsiElementVisitor() {
|
||||
@Override
|
||||
public void visitElement(PsiElement element) {
|
||||
element.acceptChildren(this);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static void ensureCorrectReparse(@NotNull PsiFile file) {
|
||||
String psiToStringDefault = DebugUtil.psiToString(file, false, false);
|
||||
String fileText = file.getText();
|
||||
DiffLog diffLog = (new BlockSupportImpl(file.getProject())).reparseRange(
|
||||
file, file.getNode(), TextRange.allOf(fileText), fileText, new EmptyProgressIndicator(), fileText);
|
||||
diffLog.performActualPsiChange(file);
|
||||
|
||||
TestCase.assertEquals(psiToStringDefault, DebugUtil.psiToString(file, false, false));
|
||||
}
|
||||
}
|
||||
-501
@@ -1,501 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2016 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.test.testFramework;
|
||||
|
||||
import com.intellij.openapi.Disposable;
|
||||
import com.intellij.openapi.application.Application;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.application.impl.ApplicationInfoImpl;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.util.Comparing;
|
||||
import com.intellij.openapi.util.Disposer;
|
||||
import com.intellij.openapi.util.io.FileSystemUtil;
|
||||
import com.intellij.openapi.util.io.FileUtil;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.openapi.vfs.CharsetToolkit;
|
||||
import com.intellij.rt.execution.junit.FileComparisonFailure;
|
||||
import com.intellij.testFramework.TestLoggerFactory;
|
||||
import com.intellij.util.ReflectionUtil;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.intellij.util.containers.hash.HashMap;
|
||||
import com.intellij.util.ui.UIUtil;
|
||||
import gnu.trove.THashSet;
|
||||
import junit.framework.AssertionFailedError;
|
||||
import junit.framework.TestCase;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.kotlin.testFramework.MockComponentManagerCreationTracer;
|
||||
import org.jetbrains.kotlin.types.FlexibleTypeImpl;
|
||||
import org.jetbrains.kotlin.utils.ExceptionUtilsKt;
|
||||
import org.junit.Assert;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.*;
|
||||
|
||||
@SuppressWarnings("UseOfSystemOutOrSystemErr")
|
||||
public abstract class KtUsefulTestCase extends TestCase {
|
||||
private static final String TEMP_DIR_MARKER = "unitTest_";
|
||||
|
||||
private static final String ORIGINAL_TEMP_DIR = FileUtil.getTempDirectory();
|
||||
|
||||
private static final Map<String, Long> TOTAL_SETUP_COST_MILLIS = new HashMap<>();
|
||||
private static final Map<String, Long> TOTAL_TEARDOWN_COST_MILLIS = new HashMap<>();
|
||||
|
||||
private Application application;
|
||||
|
||||
static {
|
||||
Logger.setFactory(TestLoggerFactory.class);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
protected final Disposable myTestRootDisposable = new TestDisposable();
|
||||
|
||||
private static final String ourPathToKeep = null;
|
||||
private final List<String> myPathsToKeep = new ArrayList<>();
|
||||
|
||||
private String myTempDir;
|
||||
|
||||
static {
|
||||
// Radar #5755208: Command line Java applications need a way to launch without a Dock icon.
|
||||
System.setProperty("apple.awt.UIElement", "true");
|
||||
|
||||
FlexibleTypeImpl.RUN_SLOW_ASSERTIONS = true;
|
||||
}
|
||||
|
||||
private boolean oldDisposerDebug;
|
||||
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
application = ApplicationManager.getApplication();
|
||||
|
||||
if (application != null && application.isDisposed()) {
|
||||
MockComponentManagerCreationTracer.diagnoseDisposedButNotClearedApplication(application);
|
||||
}
|
||||
|
||||
super.setUp();
|
||||
|
||||
String testName = FileUtil.sanitizeFileName(getTestName(true));
|
||||
if (StringUtil.isEmptyOrSpaces(testName)) testName = "";
|
||||
testName = new File(testName).getName(); // in case the test name contains file separators
|
||||
myTempDir = new File(ORIGINAL_TEMP_DIR, TEMP_DIR_MARKER + testName).getPath();
|
||||
FileUtil.resetCanonicalTempPathCache(myTempDir);
|
||||
boolean isStressTest = isStressTest();
|
||||
ApplicationInfoImpl.setInStressTest(isStressTest);
|
||||
// turn off Disposer debugging for performance tests
|
||||
oldDisposerDebug = Disposer.setDebugMode(Disposer.isDebugMode() && !isStressTest);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void tearDown() throws Exception {
|
||||
try {
|
||||
Disposer.dispose(myTestRootDisposable);
|
||||
cleanupSwingDataStructures();
|
||||
cleanupDeleteOnExitHookList();
|
||||
}
|
||||
finally {
|
||||
Disposer.setDebugMode(oldDisposerDebug);
|
||||
FileUtil.resetCanonicalTempPathCache(ORIGINAL_TEMP_DIR);
|
||||
if (hasTmpFilesToKeep()) {
|
||||
File[] files = new File(myTempDir).listFiles();
|
||||
if (files != null) {
|
||||
for (File file : files) {
|
||||
if (!shouldKeepTmpFile(file)) {
|
||||
FileUtil.delete(file);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
FileUtil.delete(new File(myTempDir));
|
||||
}
|
||||
}
|
||||
|
||||
UIUtil.removeLeakingAppleListeners();
|
||||
super.tearDown();
|
||||
|
||||
resetApplicationToNull(application);
|
||||
|
||||
application = null;
|
||||
}
|
||||
|
||||
public static void resetApplicationToNull(Application old) {
|
||||
if (old != null) return;
|
||||
resetApplicationToNull();
|
||||
}
|
||||
|
||||
public static void resetApplicationToNull() {
|
||||
try {
|
||||
Field ourApplicationField = ApplicationManager.class.getDeclaredField("ourApplication");
|
||||
ourApplicationField.setAccessible(true);
|
||||
ourApplicationField.set(null, null);
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw ExceptionUtilsKt.rethrow(e);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean hasTmpFilesToKeep() {
|
||||
return !myPathsToKeep.isEmpty();
|
||||
}
|
||||
|
||||
private boolean shouldKeepTmpFile(File file) {
|
||||
String path = file.getPath();
|
||||
if (FileUtil.pathsEqual(path, ourPathToKeep)) return true;
|
||||
for (String pathToKeep : myPathsToKeep) {
|
||||
if (FileUtil.pathsEqual(path, pathToKeep)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static final Set<String> DELETE_ON_EXIT_HOOK_DOT_FILES;
|
||||
private static final Class DELETE_ON_EXIT_HOOK_CLASS;
|
||||
static {
|
||||
Class<?> aClass;
|
||||
try {
|
||||
aClass = Class.forName("java.io.DeleteOnExitHook");
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
Set<String> files = ReflectionUtil.getStaticFieldValue(aClass, Set.class, "files");
|
||||
DELETE_ON_EXIT_HOOK_CLASS = aClass;
|
||||
DELETE_ON_EXIT_HOOK_DOT_FILES = files;
|
||||
}
|
||||
|
||||
private static void cleanupDeleteOnExitHookList() throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException {
|
||||
// try to reduce file set retained by java.io.DeleteOnExitHook
|
||||
List<String> list;
|
||||
synchronized (DELETE_ON_EXIT_HOOK_CLASS) {
|
||||
if (DELETE_ON_EXIT_HOOK_DOT_FILES.isEmpty()) return;
|
||||
list = new ArrayList<>(DELETE_ON_EXIT_HOOK_DOT_FILES);
|
||||
}
|
||||
for (int i = list.size() - 1; i >= 0; i--) {
|
||||
String path = list.get(i);
|
||||
if (FileSystemUtil.getAttributes(path) == null || new File(path).delete()) {
|
||||
synchronized (DELETE_ON_EXIT_HOOK_CLASS) {
|
||||
DELETE_ON_EXIT_HOOK_DOT_FILES.remove(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void cleanupSwingDataStructures() throws Exception {
|
||||
Object manager = ReflectionUtil.getDeclaredMethod(Class.forName("javax.swing.KeyboardManager"), "getCurrentManager").invoke(null);
|
||||
Map componentKeyStrokeMap = ReflectionUtil.getField(manager.getClass(), manager, Hashtable.class, "componentKeyStrokeMap");
|
||||
componentKeyStrokeMap.clear();
|
||||
Map containerMap = ReflectionUtil.getField(manager.getClass(), manager, Hashtable.class, "containerMap");
|
||||
containerMap.clear();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public final Disposable getTestRootDisposable() {
|
||||
return myTestRootDisposable;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void runTest() throws Throwable {
|
||||
Throwable[] throwables = new Throwable[1];
|
||||
|
||||
Runnable runnable = () -> {
|
||||
try {
|
||||
super.runTest();
|
||||
}
|
||||
catch (InvocationTargetException e) {
|
||||
e.fillInStackTrace();
|
||||
throwables[0] = e.getTargetException();
|
||||
}
|
||||
catch (IllegalAccessException e) {
|
||||
e.fillInStackTrace();
|
||||
throwables[0] = e;
|
||||
}
|
||||
catch (Throwable e) {
|
||||
throwables[0] = e;
|
||||
}
|
||||
};
|
||||
|
||||
invokeTestRunnable(runnable);
|
||||
|
||||
if (throwables[0] != null) {
|
||||
throw throwables[0];
|
||||
}
|
||||
}
|
||||
|
||||
private static void invokeTestRunnable(@NotNull Runnable runnable) throws Exception {
|
||||
EdtTestUtil.runInEdtAndWait(runnable);
|
||||
}
|
||||
|
||||
private void defaultRunBare() throws Throwable {
|
||||
Throwable exception = null;
|
||||
try {
|
||||
long setupStart = System.nanoTime();
|
||||
setUp();
|
||||
long setupCost = (System.nanoTime() - setupStart) / 1000000;
|
||||
logPerClassCost(setupCost, TOTAL_SETUP_COST_MILLIS);
|
||||
|
||||
runTest();
|
||||
TestLoggerFactory.onTestFinished(true);
|
||||
}
|
||||
catch (Throwable running) {
|
||||
TestLoggerFactory.onTestFinished(false);
|
||||
exception = running;
|
||||
}
|
||||
finally {
|
||||
try {
|
||||
long teardownStart = System.nanoTime();
|
||||
tearDown();
|
||||
long teardownCost = (System.nanoTime() - teardownStart) / 1000000;
|
||||
logPerClassCost(teardownCost, TOTAL_TEARDOWN_COST_MILLIS);
|
||||
}
|
||||
catch (Throwable tearingDown) {
|
||||
if (exception == null) exception = tearingDown;
|
||||
}
|
||||
}
|
||||
if (exception != null) throw exception;
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs the setup cost grouped by test fixture class (superclass of the current test class).
|
||||
*
|
||||
* @param cost setup cost in milliseconds
|
||||
*/
|
||||
private void logPerClassCost(long cost, Map<String, Long> costMap) {
|
||||
Class<?> superclass = getClass().getSuperclass();
|
||||
Long oldCost = costMap.get(superclass.getName());
|
||||
long newCost = oldCost == null ? cost : oldCost + cost;
|
||||
costMap.put(superclass.getName(), newCost);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void runBare() throws Throwable {
|
||||
this.defaultRunBare();
|
||||
}
|
||||
|
||||
@NonNls
|
||||
public static String toString(Iterable<?> collection) {
|
||||
if (!collection.iterator().hasNext()) {
|
||||
return "<empty>";
|
||||
}
|
||||
|
||||
StringBuilder builder = new StringBuilder();
|
||||
for (Object o : collection) {
|
||||
if (o instanceof THashSet) {
|
||||
builder.append(new TreeSet<Object>((THashSet)o));
|
||||
}
|
||||
else {
|
||||
builder.append(o);
|
||||
}
|
||||
builder.append("\n");
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
private static <T> void assertOrderedEquals(String errorMsg, @NotNull Iterable<T> actual, @NotNull T... expected) {
|
||||
Assert.assertNotNull(actual);
|
||||
Assert.assertNotNull(expected);
|
||||
assertOrderedEquals(errorMsg, actual, Arrays.asList(expected));
|
||||
}
|
||||
|
||||
public static <T> void assertOrderedEquals(
|
||||
String erroMsg,
|
||||
Iterable<? extends T> actual,
|
||||
Collection<? extends T> expected) {
|
||||
ArrayList<T> list = new ArrayList<>();
|
||||
for (T t : actual) {
|
||||
list.add(t);
|
||||
}
|
||||
if (!list.equals(new ArrayList<T>(expected))) {
|
||||
String expectedString = toString(expected);
|
||||
String actualString = toString(actual);
|
||||
Assert.assertEquals(erroMsg, expectedString, actualString);
|
||||
Assert.fail("Warning! 'toString' does not reflect the difference.\nExpected: " + expectedString + "\nActual: " + actualString);
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> void assertSameElements(T[] collection, T... expected) {
|
||||
assertSameElements(Arrays.asList(collection), expected);
|
||||
}
|
||||
|
||||
public static <T> void assertSameElements(Collection<? extends T> collection, T... expected) {
|
||||
assertSameElements(collection, Arrays.asList(expected));
|
||||
}
|
||||
|
||||
public static <T> void assertSameElements(Collection<? extends T> collection, Collection<T> expected) {
|
||||
assertSameElements(null, collection, expected);
|
||||
}
|
||||
|
||||
public static <T> void assertSameElements(String message, Collection<? extends T> collection, Collection<T> expected) {
|
||||
assertNotNull(collection);
|
||||
assertNotNull(expected);
|
||||
if (collection.size() != expected.size() || !new HashSet<>(expected).equals(new HashSet<T>(collection))) {
|
||||
Assert.assertEquals(message, toString(expected, "\n"), toString(collection, "\n"));
|
||||
Assert.assertEquals(message, new HashSet<>(expected), new HashSet<T>(collection));
|
||||
}
|
||||
}
|
||||
|
||||
public static String toString(Object[] collection, String separator) {
|
||||
return toString(Arrays.asList(collection), separator);
|
||||
}
|
||||
|
||||
public static String toString(Collection<?> collection, String separator) {
|
||||
List<String> list = ContainerUtil.map2List(collection, String::valueOf);
|
||||
Collections.sort(list);
|
||||
StringBuilder builder = new StringBuilder();
|
||||
boolean flag = false;
|
||||
for (String o : list) {
|
||||
if (flag) {
|
||||
builder.append(separator);
|
||||
}
|
||||
builder.append(o);
|
||||
flag = true;
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
@Contract("null, _ -> fail")
|
||||
public static <T> T assertInstanceOf(Object o, Class<T> aClass) {
|
||||
Assert.assertNotNull("Expected instance of: " + aClass.getName() + " actual: " + null, o);
|
||||
Assert.assertTrue("Expected instance of: " + aClass.getName() + " actual: " + o.getClass().getName(), aClass.isInstance(o));
|
||||
@SuppressWarnings("unchecked") T t = (T)o;
|
||||
return t;
|
||||
}
|
||||
|
||||
protected static <T> void assertEmpty(String errorMsg, Collection<T> collection) {
|
||||
//noinspection unchecked
|
||||
assertOrderedEquals(errorMsg, collection);
|
||||
}
|
||||
|
||||
protected static void assertSize(int expectedSize, Object[] array) {
|
||||
assertEquals(toString(Arrays.asList(array)), expectedSize, array.length);
|
||||
}
|
||||
|
||||
public static void assertSameLines(String expected, String actual) {
|
||||
String expectedText = StringUtil.convertLineSeparators(expected.trim());
|
||||
String actualText = StringUtil.convertLineSeparators(actual.trim());
|
||||
Assert.assertEquals(expectedText, actualText);
|
||||
}
|
||||
|
||||
protected String getTestName(boolean lowercaseFirstLetter) {
|
||||
return getTestName(getName(), lowercaseFirstLetter);
|
||||
}
|
||||
|
||||
public static String getTestName(String name, boolean lowercaseFirstLetter) {
|
||||
return name == null ? "" : KtPlatformTestUtil.getTestName(name, lowercaseFirstLetter);
|
||||
}
|
||||
|
||||
/** @deprecated use {@link KtPlatformTestUtil#lowercaseFirstLetter(String, boolean)} (to be removed in IDEA 17) */
|
||||
@SuppressWarnings("unused")
|
||||
public static String lowercaseFirstLetter(String name, boolean lowercaseFirstLetter) {
|
||||
return KtPlatformTestUtil.lowercaseFirstLetter(name, lowercaseFirstLetter);
|
||||
}
|
||||
|
||||
/** @deprecated use {@link KtPlatformTestUtil#isAllUppercaseName(String)} (to be removed in IDEA 17) */
|
||||
@SuppressWarnings("unused")
|
||||
public static boolean isAllUppercaseName(String name) {
|
||||
return KtPlatformTestUtil.isAllUppercaseName(name);
|
||||
}
|
||||
|
||||
public static void assertSameLinesWithFile(String filePath, String actualText) {
|
||||
assertSameLinesWithFile(filePath, actualText, true);
|
||||
}
|
||||
|
||||
public static void assertSameLinesWithFile(String filePath, String actualText, boolean trimBeforeComparing) {
|
||||
String fileText;
|
||||
try {
|
||||
fileText = FileUtil.loadFile(new File(filePath), CharsetToolkit.UTF8_CHARSET);
|
||||
}
|
||||
catch (FileNotFoundException e) {
|
||||
VfsTestUtil.overwriteTestData(filePath, actualText);
|
||||
throw new AssertionFailedError("No output text found. File " + filePath + " created.");
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
String expected = StringUtil.convertLineSeparators(trimBeforeComparing ? fileText.trim() : fileText);
|
||||
String actual = StringUtil.convertLineSeparators(trimBeforeComparing ? actualText.trim() : actualText);
|
||||
if (!Comparing.equal(expected, actual)) {
|
||||
throw new FileComparisonFailure(null, expected, actual, filePath);
|
||||
}
|
||||
}
|
||||
|
||||
public static void clearFields(Object test) throws IllegalAccessException {
|
||||
Class aClass = test.getClass();
|
||||
while (aClass != null) {
|
||||
clearDeclaredFields(test, aClass);
|
||||
aClass = aClass.getSuperclass();
|
||||
}
|
||||
}
|
||||
|
||||
private static void clearDeclaredFields(Object test, Class aClass) throws IllegalAccessException {
|
||||
if (aClass == null) return;
|
||||
for (Field field : aClass.getDeclaredFields()) {
|
||||
@NonNls String name = field.getDeclaringClass().getName();
|
||||
if (!name.startsWith("junit.framework.") && !name.startsWith("com.intellij.testFramework.")) {
|
||||
int modifiers = field.getModifiers();
|
||||
if ((modifiers & Modifier.FINAL) == 0 && (modifiers & Modifier.STATIC) == 0 && !field.getType().isPrimitive()) {
|
||||
field.setAccessible(true);
|
||||
field.set(test, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isPerformanceTest(@Nullable String testName, @Nullable String className) {
|
||||
return testName != null && testName.contains("Performance") ||
|
||||
className != null && className.contains("Performance");
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true for a test which performs A LOT of computations.
|
||||
* Such test should typically avoid performing expensive checks, e.g. data structure consistency complex validations.
|
||||
* If you want your test to be treated as "Stress", please mention one of these words in its name: "Stress", "Slow".
|
||||
* For example: {@code public void testStressPSIFromDifferentThreads()}
|
||||
*/
|
||||
|
||||
private boolean isStressTest() {
|
||||
return isStressTest(getName(), getClass().getName());
|
||||
}
|
||||
|
||||
private static boolean isStressTest(String testName, String className) {
|
||||
return isPerformanceTest(testName, className) ||
|
||||
containsStressWords(testName) ||
|
||||
containsStressWords(className);
|
||||
}
|
||||
|
||||
private static boolean containsStressWords(@Nullable String name) {
|
||||
return name != null && (name.contains("Stress") || name.contains("Slow"));
|
||||
}
|
||||
|
||||
|
||||
public class TestDisposable implements Disposable {
|
||||
@Override
|
||||
public void dispose() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
String testName = getTestName(false);
|
||||
return KtUsefulTestCase.this.getClass() + (StringUtil.isEmpty(testName) ? "" : ".test" + testName);
|
||||
}
|
||||
};
|
||||
}
|
||||
-115
@@ -1,115 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2016 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jetbrains.kotlin.test.testFramework.mock;
|
||||
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.psi.FileViewProvider;
|
||||
import com.intellij.psi.PsiDirectory;
|
||||
import com.intellij.psi.PsiFile;
|
||||
import com.intellij.psi.SingleRootFileViewProvider;
|
||||
import com.intellij.psi.impl.PsiManagerEx;
|
||||
import com.intellij.psi.impl.file.impl.FileManager;
|
||||
import com.intellij.util.containers.ConcurrentWeakFactoryMap;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.intellij.util.containers.FactoryMap;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class MockFileManager implements FileManager {
|
||||
private final PsiManagerEx myManager;
|
||||
// in mock tests it's LightVirtualFile, they're only alive when they're referenced,
|
||||
// and there can not be several instances representing the same file
|
||||
private final FactoryMap<VirtualFile, FileViewProvider> myViewProviders = new ConcurrentWeakFactoryMap<VirtualFile, FileViewProvider>() {
|
||||
@Override
|
||||
protected Map<VirtualFile, FileViewProvider> createMap() {
|
||||
return ContainerUtil.createConcurrentWeakKeyWeakValueMap();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected FileViewProvider create(VirtualFile key) {
|
||||
return new SingleRootFileViewProvider(myManager, key);
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public FileViewProvider createFileViewProvider(@NotNull VirtualFile file, boolean eventSystemEnabled) {
|
||||
return new SingleRootFileViewProvider(myManager, file, eventSystemEnabled);
|
||||
}
|
||||
|
||||
public MockFileManager(PsiManagerEx manager) {
|
||||
myManager = manager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
throw new UnsupportedOperationException("Method dispose is not yet implemented in " + getClass().getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public PsiFile findFile(@NotNull VirtualFile vFile) {
|
||||
return getCachedPsiFile(vFile);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public PsiDirectory findDirectory(@NotNull VirtualFile vFile) {
|
||||
throw new UnsupportedOperationException("Method findDirectory is not yet implemented in " + getClass().getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reloadFromDisk(@NotNull PsiFile file) //Q: move to PsiFile(Impl)?
|
||||
{
|
||||
throw new UnsupportedOperationException("Method reloadFromDisk is not yet implemented in " + getClass().getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public PsiFile getCachedPsiFile(@NotNull VirtualFile vFile) {
|
||||
FileViewProvider provider = findCachedViewProvider(vFile);
|
||||
return provider.getPsi(provider.getBaseLanguage());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cleanupForNextTest() {
|
||||
myViewProviders.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileViewProvider findViewProvider(@NotNull VirtualFile file) {
|
||||
throw new UnsupportedOperationException("Method findViewProvider is not yet implemented in " + getClass().getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileViewProvider findCachedViewProvider(@NotNull VirtualFile file) {
|
||||
return myViewProviders.get(file);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setViewProvider(@NotNull VirtualFile virtualFile, FileViewProvider fileViewProvider) {
|
||||
myViewProviders.put(virtualFile, fileViewProvider);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public List<PsiFile> getAllCachedFiles() {
|
||||
throw new UnsupportedOperationException("Method getAllCachedFiles is not yet implemented in " + getClass().getName());
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
-220
@@ -1,220 +0,0 @@
|
||||
/*
|
||||
* Copyright 2000-2018 JetBrains s.r.o. 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.formatter;
|
||||
|
||||
import com.intellij.lang.Language;
|
||||
import com.intellij.openapi.util.DefaultJDOMExternalizer;
|
||||
import com.intellij.openapi.util.DifferenceFilter;
|
||||
import com.intellij.openapi.util.InvalidDataException;
|
||||
import com.intellij.openapi.util.WriteExternalException;
|
||||
import com.intellij.psi.codeStyle.CodeStyleSettings;
|
||||
import com.intellij.psi.codeStyle.CommonCodeStyleSettings;
|
||||
import com.intellij.psi.codeStyle.LanguageCodeStyleSettingsProvider;
|
||||
import com.intellij.psi.codeStyle.arrangement.ArrangementSettings;
|
||||
import com.intellij.psi.codeStyle.arrangement.ArrangementUtil;
|
||||
import com.intellij.util.xmlb.XmlSerializer;
|
||||
import kotlin.collections.ArraysKt;
|
||||
import org.jdom.Element;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.kotlin.idea.KotlinLanguage;
|
||||
import org.jetbrains.kotlin.idea.util.ReflectionUtil;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
@SuppressWarnings("UnnecessaryFinalOnLocalVariableOrParameter")
|
||||
public class KotlinCommonCodeStyleSettings extends CommonCodeStyleSettings {
|
||||
@ReflectionUtil.SkipInEquals
|
||||
public String CODE_STYLE_DEFAULTS = null;
|
||||
|
||||
private final KotlinCommonCodeStyleSettings oldDefaultSettings = null;
|
||||
|
||||
private final boolean isTempForDeserialize;
|
||||
|
||||
public KotlinCommonCodeStyleSettings() {
|
||||
this(false);
|
||||
}
|
||||
|
||||
private KotlinCommonCodeStyleSettings(boolean isTempForDeserialize) {
|
||||
super(KotlinLanguage.INSTANCE);
|
||||
this.isTempForDeserialize = isTempForDeserialize;
|
||||
}
|
||||
|
||||
private static KotlinCommonCodeStyleSettings createForTempDeserialize() {
|
||||
return new KotlinCommonCodeStyleSettings(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readExternal(Element element) throws InvalidDataException {
|
||||
if (isTempForDeserialize) {
|
||||
super.readExternal(element);
|
||||
return;
|
||||
}
|
||||
|
||||
KotlinCommonCodeStyleSettings tempDeserialize = createForTempDeserialize();
|
||||
tempDeserialize.readExternal(element);
|
||||
|
||||
if (KotlinStyleGuideCodeStyle.CODE_STYLE_ID.equals(tempDeserialize.CODE_STYLE_DEFAULTS)) {
|
||||
KotlinStyleGuideCodeStyle.Companion.applyToCommonSettings(this, true);
|
||||
} else if (KotlinObsoleteCodeStyle.CODE_STYLE_ID.equals(tempDeserialize.CODE_STYLE_DEFAULTS)) {
|
||||
KotlinObsoleteCodeStyle.Companion.applyToCommonSettings(this, true);
|
||||
}
|
||||
|
||||
readExternalBase(element);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeExternal(Element element) throws WriteExternalException {
|
||||
CommonCodeStyleSettings defaultSettings = getDefaultSettings();
|
||||
|
||||
if (defaultSettings != null) {
|
||||
if (KotlinStyleGuideCodeStyle.CODE_STYLE_ID.equals(CODE_STYLE_DEFAULTS)) {
|
||||
KotlinStyleGuideCodeStyle.Companion.applyToCommonSettings(defaultSettings, false);
|
||||
} else if (KotlinObsoleteCodeStyle.CODE_STYLE_ID.equals(CODE_STYLE_DEFAULTS)) {
|
||||
KotlinObsoleteCodeStyle.Companion.applyToCommonSettings(defaultSettings, false);
|
||||
}
|
||||
}
|
||||
|
||||
writeExternalBase(element, defaultSettings);
|
||||
}
|
||||
|
||||
//<editor-fold desc="Copied and adapted from CommonCodeStyleSettings ">
|
||||
private void readExternalBase(Element element) throws InvalidDataException {
|
||||
super.readExternal(element);
|
||||
}
|
||||
|
||||
private void writeExternalBase(Element element, CommonCodeStyleSettings defaultSettings) throws WriteExternalException {
|
||||
Set<String> supportedFields = getSupportedFields();
|
||||
if (supportedFields != null) {
|
||||
supportedFields.add("PARENT_SETTINGS_INSTALLED");
|
||||
supportedFields.add("FORCE_REARRANGE_MODE");
|
||||
supportedFields.add("CODE_STYLE_DEFAULTS");
|
||||
}
|
||||
//noinspection deprecation
|
||||
DefaultJDOMExternalizer.writeExternal(this, element, new SupportedFieldsDiffFilter(this, supportedFields, defaultSettings));
|
||||
|
||||
IndentOptions myIndentOptions = getIndentOptions();
|
||||
if (myIndentOptions != null) {
|
||||
IndentOptions defaultIndentOptions = defaultSettings != null ? defaultSettings.getIndentOptions() : null;
|
||||
Element indentOptionsElement = new Element(INDENT_OPTIONS_TAG);
|
||||
myIndentOptions.serialize(indentOptionsElement, defaultIndentOptions);
|
||||
if (!indentOptionsElement.getChildren().isEmpty()) {
|
||||
element.addContent(indentOptionsElement);
|
||||
}
|
||||
}
|
||||
|
||||
ArrangementSettings myArrangementSettings = getArrangementSettings();
|
||||
if (myArrangementSettings != null) {
|
||||
Element container = new Element(ARRANGEMENT_ELEMENT_NAME);
|
||||
ArrangementUtil.writeExternal(container, myArrangementSettings, myLanguage);
|
||||
if (!container.getChildren().isEmpty()) {
|
||||
element.addContent(container);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommonCodeStyleSettings clone(@NotNull CodeStyleSettings rootSettings) {
|
||||
CommonCodeStyleSettings commonSettings = new KotlinCommonCodeStyleSettings();
|
||||
copyPublicFieldsOwn(this, commonSettings);
|
||||
|
||||
try {
|
||||
Method setRootSettingsMethod = CommonCodeStyleSettings.class.getDeclaredMethod("setRootSettings", CodeStyleSettings.class);
|
||||
setRootSettingsMethod.setAccessible(true);
|
||||
setRootSettingsMethod.invoke(commonSettings, rootSettings);
|
||||
}
|
||||
catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
|
||||
commonSettings.setForceArrangeMenuAvailable(isForceArrangeMenuAvailable());
|
||||
|
||||
IndentOptions indentOptions = getIndentOptions();
|
||||
if (indentOptions != null) {
|
||||
IndentOptions targetIndentOptions = commonSettings.initIndentOptions();
|
||||
targetIndentOptions.copyFrom(indentOptions);
|
||||
}
|
||||
|
||||
ArrangementSettings arrangementSettings = getArrangementSettings();
|
||||
if (arrangementSettings != null) {
|
||||
commonSettings.setArrangementSettings(arrangementSettings.clone());
|
||||
}
|
||||
|
||||
return commonSettings;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (!(obj instanceof KotlinCommonCodeStyleSettings)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!ReflectionUtil.comparePublicNonFinalFieldsWithSkip(this, obj)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
CommonCodeStyleSettings other = (CommonCodeStyleSettings) obj;
|
||||
|
||||
IndentOptions options = getIndentOptions();
|
||||
if ((options == null && other.getIndentOptions() != null) ||
|
||||
(options != null && !options.equals(other.getIndentOptions()))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return arrangementSettingsEqual(other);
|
||||
}
|
||||
//</editor-fold>
|
||||
|
||||
//<editor-fold desc="Copied from CommonCodeStyleSettings">
|
||||
private static final String INDENT_OPTIONS_TAG = "indentOptions";
|
||||
private static final String ARRANGEMENT_ELEMENT_NAME = "arrangement";
|
||||
|
||||
private final Language myLanguage = KotlinLanguage.INSTANCE;
|
||||
|
||||
@Nullable
|
||||
private CommonCodeStyleSettings getDefaultSettings() {
|
||||
return LanguageCodeStyleSettingsProvider.getDefaultCommonSettings(myLanguage);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private Set<String> getSupportedFields() {
|
||||
final LanguageCodeStyleSettingsProvider provider = LanguageCodeStyleSettingsProvider.forLanguage(myLanguage);
|
||||
return provider == null ? null : provider.getSupportedFields();
|
||||
}
|
||||
|
||||
private static class SupportedFieldsDiffFilter extends DifferenceFilter<CommonCodeStyleSettings> {
|
||||
private final Set<String> mySupportedFieldNames;
|
||||
|
||||
public SupportedFieldsDiffFilter(
|
||||
final CommonCodeStyleSettings object,
|
||||
Set<String> supportedFiledNames,
|
||||
final CommonCodeStyleSettings parentObject
|
||||
) {
|
||||
super(object, parentObject);
|
||||
mySupportedFieldNames = supportedFiledNames;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAccept(@NotNull Field field) {
|
||||
if (mySupportedFieldNames == null ||
|
||||
mySupportedFieldNames.contains(field.getName())) {
|
||||
return super.isAccept(field);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Can't use super.copyPublicFields because the method is internal in 181
|
||||
private static void copyPublicFieldsOwn(Object from, Object to) {
|
||||
assert from != to;
|
||||
com.intellij.util.ReflectionUtil.copyFields(to.getClass().getFields(), from, to);
|
||||
}
|
||||
//</editor-fold>
|
||||
}
|
||||
-16
@@ -1,16 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2018 JetBrains s.r.o. 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.compatibility
|
||||
|
||||
import com.intellij.openapi.Disposable
|
||||
import com.intellij.testFramework.fixtures.CodeInsightTestFixture
|
||||
|
||||
/**
|
||||
* Method was introduced in 173 idea. Should be dropped after abandoning 172 branch.
|
||||
* BUNCH: 173
|
||||
*/
|
||||
@Suppress("IncompatibleAPI")
|
||||
val CodeInsightTestFixture.projectDisposableEx: Disposable get() = project
|
||||
-113
@@ -1,113 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2016 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.compiler.configuration
|
||||
|
||||
import com.intellij.openapi.application.ReadAction
|
||||
import com.intellij.openapi.components.PersistentStateComponent
|
||||
import com.intellij.openapi.components.StoragePathMacros.PROJECT_CONFIG_DIR
|
||||
import com.intellij.openapi.progress.ProcessCanceledException
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.util.ReflectionUtil
|
||||
import com.intellij.util.messages.Topic
|
||||
import com.intellij.util.xmlb.SkipDefaultValuesSerializationFilters
|
||||
import com.intellij.util.xmlb.XmlSerializer
|
||||
import org.jdom.Element
|
||||
import org.jetbrains.kotlin.cli.common.arguments.*
|
||||
import org.jetbrains.kotlin.config.SettingConstants
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
abstract class BaseKotlinCompilerSettings<T : Freezable> protected constructor(private val project: Project) : PersistentStateComponent<Element>, Cloneable {
|
||||
@Suppress("LeakingThis", "UNCHECKED_CAST")
|
||||
private var _settings: T = createSettings().frozen() as T
|
||||
private set(value) {
|
||||
field = value.frozen() as T
|
||||
}
|
||||
|
||||
var settings: T
|
||||
get() = _settings
|
||||
set(value) {
|
||||
validateNewSettings(value)
|
||||
_settings = value
|
||||
|
||||
syncPublisher(project, KotlinCompilerSettingsListener.TOPIC).settingsChanged(value)
|
||||
}
|
||||
|
||||
fun update(changer: T.() -> Unit) {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
settings = (settings.unfrozen() as T).apply { changer() }
|
||||
}
|
||||
|
||||
protected fun validateInheritedFieldsUnchanged(settings: T) {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
val inheritedProperties = collectProperties<T>(settings::class as KClass<T>, true)
|
||||
val defaultInstance = createSettings()
|
||||
val invalidFields = inheritedProperties.filter { it.get(settings) != it.get(defaultInstance) }
|
||||
if (invalidFields.isNotEmpty()) {
|
||||
throw IllegalArgumentException("Following fields are expected to be left unchanged in ${settings.javaClass}: ${invalidFields.joinToString { it.name }}")
|
||||
}
|
||||
}
|
||||
|
||||
protected open fun validateNewSettings(settings: T) {
|
||||
|
||||
}
|
||||
|
||||
protected abstract fun createSettings(): T
|
||||
|
||||
override fun getState() = XmlSerializer.serialize(_settings, SKIP_DEFAULT_VALUES)
|
||||
|
||||
override fun loadState(state: Element) {
|
||||
_settings = ReflectionUtil.newInstance(_settings.javaClass).apply {
|
||||
if (this is CommonCompilerArguments) {
|
||||
freeArgs = ArrayList()
|
||||
}
|
||||
XmlSerializer.deserializeInto(this, state)
|
||||
}
|
||||
|
||||
syncPublisher(project, KotlinCompilerSettingsListener.TOPIC).settingsChanged(settings)
|
||||
}
|
||||
|
||||
public override fun clone(): Any = super.clone()
|
||||
|
||||
companion object {
|
||||
const val KOTLIN_COMPILER_SETTINGS_PATH = PROJECT_CONFIG_DIR + "/" + SettingConstants.KOTLIN_COMPILER_SETTINGS_FILE
|
||||
|
||||
private val SKIP_DEFAULT_VALUES = SkipDefaultValuesSerializationFilters(
|
||||
CommonCompilerArguments.DummyImpl(),
|
||||
K2JVMCompilerArguments(),
|
||||
K2JSCompilerArguments().apply {
|
||||
sourceMapPrefix = ""
|
||||
sourceMapEmbedSources = K2JsArgumentConstants.SOURCE_MAP_SOURCE_CONTENT_INLINING
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
interface KotlinCompilerSettingsListener {
|
||||
fun <T> settingsChanged(newSettings: T)
|
||||
|
||||
companion object {
|
||||
val TOPIC = Topic.create("KotlinCompilerSettingsListener", KotlinCompilerSettingsListener::class.java)
|
||||
}
|
||||
}
|
||||
|
||||
// copied from BackgroundTaskUtils.syncPublisher in IDEA 181
|
||||
private fun syncPublisher(project: Project, topic: Topic<KotlinCompilerSettingsListener>): KotlinCompilerSettingsListener {
|
||||
return ReadAction.compute<KotlinCompilerSettingsListener, ProcessCanceledException> {
|
||||
if (project.isDisposed) throw ProcessCanceledException()
|
||||
project.messageBus.syncPublisher(topic)
|
||||
}
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2018 JetBrains s.r.o. 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.core.util.compat.psi
|
||||
|
||||
import com.intellij.psi.PsiMethod
|
||||
import com.intellij.psi.util.PsiUtil
|
||||
|
||||
/**
|
||||
* Method is absent in 172.
|
||||
* BUNCH: 173
|
||||
*/
|
||||
@Suppress("IncompatibleAPI")
|
||||
fun canBeOverridden(method: PsiMethod): Boolean {
|
||||
return PsiUtil.canBeOverriden(method)
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
@file:JvmName("GradleKotlinDSLConstants")
|
||||
|
||||
package org.jetbrains.kotlin.gradle.kdsl
|
||||
|
||||
const val DEFAULT_SCRIPT_NAME = "build.gradle.kts"
|
||||
-55
@@ -1,55 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="org.jetbrains.kotlin.gradle.kdsl.GradleFrameworksWizardStep">
|
||||
<grid id="27dc6" binding="myPanel" layout-manager="GridLayoutManager" row-count="1" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
|
||||
<margin top="0" left="0" bottom="0" right="0"/>
|
||||
<constraints>
|
||||
<xy x="20" y="20" width="500" height="400"/>
|
||||
</constraints>
|
||||
<properties/>
|
||||
<border type="none"/>
|
||||
<children>
|
||||
<grid id="22e59" layout-manager="BorderLayout" hgap="0" vgap="0">
|
||||
<constraints>
|
||||
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="7" hsize-policy="7" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
|
||||
</constraints>
|
||||
<properties/>
|
||||
<border type="none"/>
|
||||
<children>
|
||||
<grid id="ad16" binding="myOptionsPanel" layout-manager="CardLayout" hgap="0" vgap="0">
|
||||
<constraints border-constraint="Center"/>
|
||||
<properties/>
|
||||
<clientProperties>
|
||||
<BorderFactoryClass class="java.lang.String" value="com.intellij.ui.IdeBorderFactory$PlainSmallWithoutIndent"/>
|
||||
</clientProperties>
|
||||
<border type="none"/>
|
||||
<children>
|
||||
<grid id="27a92" layout-manager="BorderLayout" hgap="0" vgap="0">
|
||||
<constraints>
|
||||
<card name="templates card"/>
|
||||
</constraints>
|
||||
<properties/>
|
||||
<border type="none"/>
|
||||
<children/>
|
||||
</grid>
|
||||
<grid id="b50ee" binding="myFrameworksPanelPlaceholder" layout-manager="BorderLayout" hgap="0" vgap="0">
|
||||
<constraints>
|
||||
<card name="frameworks card"/>
|
||||
</constraints>
|
||||
<properties/>
|
||||
<border type="none"/>
|
||||
<children>
|
||||
<component id="916d9" class="com.intellij.ui.components.JBLabel" binding="myFrameworksLabel">
|
||||
<constraints border-constraint="North"/>
|
||||
<properties>
|
||||
<text value="Additional Libraries and &Frameworks:"/>
|
||||
</properties>
|
||||
</component>
|
||||
</children>
|
||||
</grid>
|
||||
</children>
|
||||
</grid>
|
||||
</children>
|
||||
</grid>
|
||||
</children>
|
||||
</grid>
|
||||
</form>
|
||||
-101
@@ -1,101 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jetbrains.kotlin.gradle.kdsl;
|
||||
|
||||
import com.intellij.framework.addSupport.FrameworkSupportInModuleProvider;
|
||||
import com.intellij.ide.util.newProjectWizard.AddSupportForFrameworksPanel;
|
||||
import com.intellij.ide.util.newProjectWizard.impl.FrameworkSupportModelBase;
|
||||
import com.intellij.ide.util.projectWizard.ModuleBuilder;
|
||||
import com.intellij.ide.util.projectWizard.ModuleWizardStep;
|
||||
import com.intellij.ide.util.projectWizard.WizardContext;
|
||||
import com.intellij.openapi.Disposable;
|
||||
import com.intellij.openapi.module.Module;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.roots.ModifiableRootModel;
|
||||
import com.intellij.openapi.roots.ui.configuration.projectRoot.LibrariesContainer;
|
||||
import com.intellij.openapi.roots.ui.configuration.projectRoot.LibrariesContainerFactory;
|
||||
import com.intellij.openapi.util.Disposer;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.ui.components.JBLabel;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.kotlin.gradle.kdsl.frameworkSupport.GradleFrameworkSupportProvider;
|
||||
import org.jetbrains.kotlin.gradle.kdsl.frameworkSupport.GradleJavaFrameworkSupportProvider;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class GradleFrameworksWizardStep extends ModuleWizardStep implements Disposable {
|
||||
|
||||
private JPanel myPanel;
|
||||
private final AddSupportForFrameworksPanel myFrameworksPanel;
|
||||
private JPanel myFrameworksPanelPlaceholder;
|
||||
private JPanel myOptionsPanel;
|
||||
@SuppressWarnings("unused") private JBLabel myFrameworksLabel;
|
||||
|
||||
public GradleFrameworksWizardStep(WizardContext context, final ModuleBuilder builder) {
|
||||
|
||||
Project project = context.getProject();
|
||||
final LibrariesContainer container = LibrariesContainerFactory.createContainer(context.getProject());
|
||||
FrameworkSupportModelBase model = new FrameworkSupportModelBase(project, null, container) {
|
||||
@NotNull
|
||||
@Override
|
||||
public String getBaseDirectoryForLibrariesPath() {
|
||||
return StringUtil.notNullize(builder.getContentEntryPath());
|
||||
}
|
||||
};
|
||||
|
||||
myFrameworksPanel =
|
||||
new AddSupportForFrameworksPanel(Collections.emptyList(), model, true, null);
|
||||
|
||||
List<FrameworkSupportInModuleProvider> providers = ContainerUtil.newArrayList();
|
||||
Collections.addAll(providers, GradleFrameworkSupportProvider.EP_NAME.getExtensions());
|
||||
|
||||
myFrameworksPanel.setProviders(providers, Collections.emptySet(), Collections.singleton(GradleJavaFrameworkSupportProvider.ID));
|
||||
Disposer.register(this, myFrameworksPanel);
|
||||
myFrameworksPanelPlaceholder.add(myFrameworksPanel.getMainPanel());
|
||||
|
||||
ModuleBuilder.ModuleConfigurationUpdater configurationUpdater = new ModuleBuilder.ModuleConfigurationUpdater() {
|
||||
@Override
|
||||
public void update(@NotNull Module module, @NotNull ModifiableRootModel rootModel) {
|
||||
myFrameworksPanel.addSupport(module, rootModel);
|
||||
}
|
||||
};
|
||||
builder.addModuleConfigurationUpdater(configurationUpdater);
|
||||
|
||||
((CardLayout)myOptionsPanel.getLayout()).show(myOptionsPanel, "frameworks card");
|
||||
}
|
||||
|
||||
@Override
|
||||
public JComponent getComponent() {
|
||||
return myPanel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateDataModel() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disposeUIResources() {
|
||||
Disposer.dispose(this);
|
||||
}
|
||||
}
|
||||
@@ -1,491 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jetbrains.kotlin.gradle.kdsl;
|
||||
|
||||
import com.intellij.ide.fileTemplates.FileTemplate;
|
||||
import com.intellij.ide.fileTemplates.FileTemplateManager;
|
||||
import com.intellij.ide.highlighter.ModuleFileType;
|
||||
import com.intellij.ide.projectWizard.ProjectSettingsStep;
|
||||
import com.intellij.ide.util.EditorHelper;
|
||||
import com.intellij.ide.util.projectWizard.JavaModuleBuilder;
|
||||
import com.intellij.ide.util.projectWizard.ModuleWizardStep;
|
||||
import com.intellij.ide.util.projectWizard.SettingsStep;
|
||||
import com.intellij.ide.util.projectWizard.WizardContext;
|
||||
import com.intellij.openapi.Disposable;
|
||||
import com.intellij.openapi.application.ModalityState;
|
||||
import com.intellij.openapi.components.StorageScheme;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.externalSystem.importing.ImportSpec;
|
||||
import com.intellij.openapi.externalSystem.importing.ImportSpecBuilder;
|
||||
import com.intellij.openapi.externalSystem.model.ExternalSystemDataKeys;
|
||||
import com.intellij.openapi.externalSystem.model.project.ProjectData;
|
||||
import com.intellij.openapi.externalSystem.model.project.ProjectId;
|
||||
import com.intellij.openapi.externalSystem.service.execution.ProgressExecutionMode;
|
||||
import com.intellij.openapi.externalSystem.service.project.manage.ExternalProjectsManagerImpl;
|
||||
import com.intellij.openapi.externalSystem.service.project.wizard.AbstractExternalModuleBuilder;
|
||||
import com.intellij.openapi.externalSystem.service.project.wizard.ExternalModuleSettingsStep;
|
||||
import com.intellij.openapi.externalSystem.settings.AbstractExternalSystemSettings;
|
||||
import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil;
|
||||
import com.intellij.openapi.externalSystem.util.ExternalSystemUtil;
|
||||
import com.intellij.openapi.fileEditor.FileDocumentManager;
|
||||
import com.intellij.openapi.fileEditor.impl.LoadTextUtil;
|
||||
import com.intellij.openapi.module.*;
|
||||
import com.intellij.openapi.options.ConfigurationException;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.project.ex.ProjectManagerEx;
|
||||
import com.intellij.openapi.projectRoots.JavaSdkType;
|
||||
import com.intellij.openapi.projectRoots.SdkTypeId;
|
||||
import com.intellij.openapi.roots.ModifiableRootModel;
|
||||
import com.intellij.openapi.roots.ui.configuration.ModulesProvider;
|
||||
import com.intellij.openapi.util.Disposer;
|
||||
import com.intellij.openapi.util.InvalidDataException;
|
||||
import com.intellij.openapi.util.Key;
|
||||
import com.intellij.openapi.util.io.FileUtil;
|
||||
import com.intellij.openapi.util.io.FileUtilRt;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.openapi.vfs.LocalFileSystem;
|
||||
import com.intellij.openapi.vfs.VfsUtil;
|
||||
import com.intellij.openapi.vfs.VfsUtilCore;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.psi.PsiFile;
|
||||
import com.intellij.psi.PsiManager;
|
||||
import com.intellij.psi.codeStyle.CodeStyleSettingsManager;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import org.gradle.util.GradleVersion;
|
||||
import org.jdom.JDOMException;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.kotlin.gradle.kdsl.frameworkSupport.BuildScriptDataBuilder;
|
||||
import org.jetbrains.kotlin.gradle.kdsl.frameworkSupport.KotlinBuildScriptDataBuilder;
|
||||
import org.jetbrains.plugins.gradle.service.settings.GradleProjectSettingsControl;
|
||||
import org.jetbrains.plugins.gradle.settings.DistributionType;
|
||||
import org.jetbrains.plugins.gradle.settings.GradleProjectSettings;
|
||||
import org.jetbrains.plugins.gradle.util.GradleConstants;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.jetbrains.kotlin.gradle.kdsl.GradleKotlinDSLConstants.DEFAULT_SCRIPT_NAME;
|
||||
|
||||
public class GradleModuleBuilder extends AbstractExternalModuleBuilder<GradleProjectSettings> {
|
||||
|
||||
private static final Logger LOG = Logger.getInstance(GradleModuleBuilder.class);
|
||||
|
||||
private static final String TEMPLATE_GRADLE_SETTINGS = "Gradle Kotlin DSL Settings.gradle";
|
||||
private static final String TEMPLATE_GRADLE_SETTINGS_MERGE = "Gradle Settings merge.gradle";
|
||||
private static final String TEMPLATE_GRADLE_BUILD_WITH_WRAPPER = "Gradle Build Script with wrapper.gradle";
|
||||
private static final String DEFAULT_TEMPLATE_GRADLE_BUILD = "Gradle Kotlin DSL Build Script.gradle";
|
||||
|
||||
private static final String TEMPLATE_ATTRIBUTE_PROJECT_NAME = "PROJECT_NAME";
|
||||
private static final String TEMPLATE_ATTRIBUTE_MODULE_PATH = "MODULE_PATH";
|
||||
private static final String TEMPLATE_ATTRIBUTE_MODULE_FLAT_DIR = "MODULE_FLAT_DIR";
|
||||
private static final String TEMPLATE_ATTRIBUTE_MODULE_NAME = "MODULE_NAME";
|
||||
private static final String TEMPLATE_ATTRIBUTE_MODULE_GROUP = "MODULE_GROUP";
|
||||
private static final String TEMPLATE_ATTRIBUTE_MODULE_VERSION = "MODULE_VERSION";
|
||||
private static final String TEMPLATE_ATTRIBUTE_GRADLE_VERSION = "GRADLE_VERSION";
|
||||
private static final String TEMPLATE_ATTRIBUTE_BUILD_FILE_NAME = "BUILD_FILE_NAME";
|
||||
private static final Key<KotlinBuildScriptDataBuilder> BUILD_SCRIPT_DATA =
|
||||
Key.create("gradle.module.kotlinBuildScriptData");
|
||||
|
||||
private WizardContext myWizardContext;
|
||||
|
||||
@Nullable
|
||||
private ProjectData myParentProject;
|
||||
private boolean myInheritGroupId;
|
||||
private boolean myInheritVersion;
|
||||
private ProjectId myProjectId;
|
||||
private String rootProjectPath;
|
||||
|
||||
public GradleModuleBuilder() {
|
||||
super(GradleConstants.SYSTEM_ID, new GradleProjectSettings());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPresentableName() {
|
||||
return "Gradle (Kotlin DSL)";
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Module createModule(@NotNull ModifiableModuleModel moduleModel)
|
||||
throws InvalidDataException, IOException, ModuleWithNameAlreadyExists, JDOMException, ConfigurationException {
|
||||
LOG.assertTrue(getName() != null);
|
||||
final String originModuleFilePath = getModuleFilePath();
|
||||
LOG.assertTrue(originModuleFilePath != null);
|
||||
|
||||
String moduleName;
|
||||
if (myProjectId == null) {
|
||||
moduleName = getName();
|
||||
}
|
||||
else {
|
||||
moduleName = ModuleGrouperKt.isQualifiedModuleNamesEnabled() && StringUtil.isNotEmpty(myProjectId.getGroupId())
|
||||
? (myProjectId.getGroupId() + '.' + myProjectId.getArtifactId())
|
||||
: myProjectId.getArtifactId();
|
||||
}
|
||||
Project contextProject = myWizardContext.getProject();
|
||||
String projectFileDirectory = null;
|
||||
if (myWizardContext.isCreatingNewProject() || contextProject == null || contextProject.getBasePath() == null) {
|
||||
projectFileDirectory = myWizardContext.getProjectFileDirectory();
|
||||
}
|
||||
else if (myWizardContext.getProjectStorageFormat() == StorageScheme.DEFAULT) {
|
||||
String moduleFileDirectory = getModuleFileDirectory();
|
||||
if (moduleFileDirectory != null) {
|
||||
projectFileDirectory = moduleFileDirectory;
|
||||
}
|
||||
}
|
||||
if (projectFileDirectory == null) {
|
||||
projectFileDirectory = contextProject.getBasePath();
|
||||
}
|
||||
if (myWizardContext.getProjectStorageFormat() == StorageScheme.DIRECTORY_BASED) {
|
||||
projectFileDirectory += "/.idea/modules";
|
||||
}
|
||||
String moduleFilePath = projectFileDirectory + "/" + moduleName + ModuleFileType.DOT_DEFAULT_EXTENSION;
|
||||
deleteModuleFile(moduleFilePath);
|
||||
final ModuleType moduleType = getModuleType();
|
||||
final Module module = moduleModel.newModule(moduleFilePath, moduleType.getId());
|
||||
setupModule(module);
|
||||
return module;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setupRootModel(final ModifiableRootModel modifiableRootModel) throws ConfigurationException {
|
||||
String contentEntryPath = getContentEntryPath();
|
||||
if (StringUtil.isEmpty(contentEntryPath)) {
|
||||
return;
|
||||
}
|
||||
File contentRootDir = new File(contentEntryPath);
|
||||
FileUtilRt.createDirectory(contentRootDir);
|
||||
LocalFileSystem fileSystem = LocalFileSystem.getInstance();
|
||||
VirtualFile modelContentRootDir = fileSystem.refreshAndFindFileByIoFile(contentRootDir);
|
||||
if (modelContentRootDir == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
modifiableRootModel.addContentEntry(modelContentRootDir);
|
||||
// todo this should be moved to generic ModuleBuilder
|
||||
if (myJdk != null) {
|
||||
modifiableRootModel.setSdk(myJdk);
|
||||
}
|
||||
else {
|
||||
modifiableRootModel.inheritSdk();
|
||||
}
|
||||
|
||||
final Project project = modifiableRootModel.getProject();
|
||||
if (myParentProject != null) {
|
||||
rootProjectPath = myParentProject.getLinkedExternalProjectPath();
|
||||
}
|
||||
else {
|
||||
rootProjectPath =
|
||||
FileUtil.toCanonicalPath(myWizardContext.isCreatingNewProject() ? project.getBasePath() : modelContentRootDir.getPath());
|
||||
}
|
||||
assert rootProjectPath != null;
|
||||
|
||||
final VirtualFile gradleBuildFile = setupGradleBuildFile(modelContentRootDir);
|
||||
setupGradleSettingsFile(
|
||||
rootProjectPath, modelContentRootDir, modifiableRootModel.getProject().getName(),
|
||||
myProjectId == null ? modifiableRootModel.getModule().getName() : myProjectId.getArtifactId(),
|
||||
myWizardContext.isCreatingNewProject() || myParentProject == null
|
||||
);
|
||||
|
||||
if (gradleBuildFile != null) {
|
||||
modifiableRootModel.getModule().putUserData(
|
||||
BUILD_SCRIPT_DATA, new KotlinBuildScriptDataBuilder(gradleBuildFile));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setupModule(Module module) throws ConfigurationException {
|
||||
super.setupModule(module);
|
||||
assert rootProjectPath != null;
|
||||
|
||||
VirtualFile buildScriptFile = null;
|
||||
final BuildScriptDataBuilder buildScriptDataBuilder = getBuildScriptData(module);
|
||||
try {
|
||||
if (buildScriptDataBuilder != null) {
|
||||
buildScriptFile = buildScriptDataBuilder.getBuildScriptFile();
|
||||
String lineSeparator = lineSeparator(buildScriptFile);
|
||||
String configurationPart = StringUtil.convertLineSeparators(buildScriptDataBuilder.buildConfigurationPart(), lineSeparator);
|
||||
String existingText = StringUtil.trimTrailing(VfsUtilCore.loadText(buildScriptFile));
|
||||
String content = (!configurationPart.isEmpty() ? configurationPart + lineSeparator : "") +
|
||||
(!existingText.isEmpty() ? existingText + lineSeparator : "") +
|
||||
lineSeparator +
|
||||
StringUtil.convertLineSeparators(buildScriptDataBuilder.buildMainPart(), lineSeparator);
|
||||
VfsUtil.saveText(buildScriptFile, content);
|
||||
}
|
||||
}
|
||||
catch (IOException e) {
|
||||
LOG.warn("Unexpected exception on applying frameworks templates", e);
|
||||
}
|
||||
|
||||
final Project project = module.getProject();
|
||||
if (myWizardContext.isCreatingNewProject()) {
|
||||
getExternalProjectSettings().setExternalProjectPath(rootProjectPath);
|
||||
AbstractExternalSystemSettings settings = ExternalSystemApiUtil.getSettings(project, GradleConstants.SYSTEM_ID);
|
||||
project.putUserData(ExternalSystemDataKeys.NEWLY_CREATED_PROJECT, Boolean.TRUE);
|
||||
//noinspection unchecked
|
||||
settings.linkProject(getExternalProjectSettings());
|
||||
}
|
||||
else {
|
||||
FileDocumentManager.getInstance().saveAllDocuments();
|
||||
final GradleProjectSettings gradleProjectSettings = getExternalProjectSettings();
|
||||
final VirtualFile finalBuildScriptFile = buildScriptFile;
|
||||
Runnable runnable = () -> {
|
||||
if (myParentProject == null) {
|
||||
gradleProjectSettings.setExternalProjectPath(rootProjectPath);
|
||||
AbstractExternalSystemSettings settings = ExternalSystemApiUtil.getSettings(project, GradleConstants.SYSTEM_ID);
|
||||
//noinspection unchecked
|
||||
settings.linkProject(gradleProjectSettings);
|
||||
}
|
||||
|
||||
ImportSpec importSpec = new ImportSpecBuilder(project, GradleConstants.SYSTEM_ID)
|
||||
.use(ProgressExecutionMode.IN_BACKGROUND_ASYNC)
|
||||
.createDirectoriesForEmptyContentRoots()
|
||||
.useDefaultCallback()
|
||||
.build();
|
||||
ExternalSystemUtil.refreshProject(rootProjectPath, importSpec);
|
||||
|
||||
final PsiFile psiFile;
|
||||
if (finalBuildScriptFile != null) {
|
||||
psiFile = PsiManager.getInstance(project).findFile(finalBuildScriptFile);
|
||||
if (psiFile != null) {
|
||||
EditorHelper.openInEditor(psiFile);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// execute when current dialog is closed
|
||||
ExternalSystemUtil.invokeLater(project, ModalityState.NON_MODAL, runnable);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ModuleWizardStep[] createWizardSteps(@NotNull WizardContext wizardContext, @NotNull ModulesProvider modulesProvider) {
|
||||
myWizardContext = wizardContext;
|
||||
return new ModuleWizardStep[]{
|
||||
new GradleModuleWizardStep(this, wizardContext),
|
||||
new ExternalModuleSettingsStep<>(
|
||||
wizardContext, this, new GradleProjectSettingsControl(getExternalProjectSettings()))
|
||||
};
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public ModuleWizardStep getCustomOptionsStep(WizardContext context, Disposable parentDisposable) {
|
||||
final GradleFrameworksWizardStep step = new GradleFrameworksWizardStep(context, this);
|
||||
Disposer.register(parentDisposable, step);
|
||||
return step;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSuitableSdkType(SdkTypeId sdk) {
|
||||
return sdk instanceof JavaSdkType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getParentGroup() {
|
||||
return JavaModuleType.BUILD_TOOLS_GROUP;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getWeight() {
|
||||
return JavaModuleBuilder.BUILD_SYSTEM_WEIGHT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ModuleType getModuleType() {
|
||||
return StdModuleTypes.JAVA;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private VirtualFile setupGradleBuildFile(@NotNull VirtualFile modelContentRootDir)
|
||||
throws ConfigurationException {
|
||||
final VirtualFile file = getOrCreateExternalProjectConfigFile(modelContentRootDir.getPath(), GradleKotlinDSLConstants.DEFAULT_SCRIPT_NAME);
|
||||
|
||||
if (file != null) {
|
||||
final String templateName = getExternalProjectSettings().getDistributionType() == DistributionType.WRAPPED
|
||||
? TEMPLATE_GRADLE_BUILD_WITH_WRAPPER
|
||||
: DEFAULT_TEMPLATE_GRADLE_BUILD;
|
||||
Map<String, String> attributes = ContainerUtil.newHashMap();
|
||||
if (myProjectId != null) {
|
||||
attributes.put(TEMPLATE_ATTRIBUTE_MODULE_VERSION, myProjectId.getVersion());
|
||||
attributes.put(TEMPLATE_ATTRIBUTE_MODULE_GROUP, myProjectId.getGroupId());
|
||||
attributes.put(TEMPLATE_ATTRIBUTE_GRADLE_VERSION, GradleVersion.current().getVersion());
|
||||
}
|
||||
saveFile(file, templateName, attributes);
|
||||
}
|
||||
return file;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static VirtualFile setupGradleSettingsFile(@NotNull String rootProjectPath,
|
||||
@NotNull VirtualFile modelContentRootDir,
|
||||
String projectName,
|
||||
String moduleName,
|
||||
boolean renderNewFile)
|
||||
throws ConfigurationException {
|
||||
final VirtualFile file = getOrCreateExternalProjectConfigFile(rootProjectPath, GradleConstants.SETTINGS_FILE_NAME);
|
||||
if (file == null) return null;
|
||||
|
||||
if (renderNewFile) {
|
||||
final String moduleDirName = VfsUtilCore.getRelativePath(modelContentRootDir, file.getParent(), '/');
|
||||
|
||||
Map<String, String> attributes = ContainerUtil.newHashMap();
|
||||
attributes.put(TEMPLATE_ATTRIBUTE_PROJECT_NAME, projectName);
|
||||
attributes.put(TEMPLATE_ATTRIBUTE_MODULE_PATH, moduleDirName);
|
||||
attributes.put(TEMPLATE_ATTRIBUTE_MODULE_NAME, moduleName);
|
||||
attributes.put(TEMPLATE_ATTRIBUTE_BUILD_FILE_NAME, DEFAULT_SCRIPT_NAME); // TODO: gradle > 4.0 doesn't need this
|
||||
saveFile(file, TEMPLATE_GRADLE_SETTINGS, attributes);
|
||||
}
|
||||
else {
|
||||
char separatorChar = file.getParent() == null || !VfsUtilCore.isAncestor(file.getParent(), modelContentRootDir, true) ? '/' : ':';
|
||||
String modulePath = VfsUtil.getPath(file, modelContentRootDir, separatorChar);
|
||||
|
||||
Map<String, String> attributes = ContainerUtil.newHashMap();
|
||||
attributes.put(TEMPLATE_ATTRIBUTE_MODULE_NAME, moduleName);
|
||||
// check for flat structure
|
||||
final String flatStructureModulePath =
|
||||
modulePath != null && StringUtil.startsWith(modulePath, "../") ? StringUtil.trimStart(modulePath, "../") : null;
|
||||
if (StringUtil.equals(flatStructureModulePath, modelContentRootDir.getName())) {
|
||||
attributes.put(TEMPLATE_ATTRIBUTE_MODULE_FLAT_DIR, "true");
|
||||
attributes.put(TEMPLATE_ATTRIBUTE_MODULE_PATH, flatStructureModulePath);
|
||||
}
|
||||
else {
|
||||
attributes.put(TEMPLATE_ATTRIBUTE_MODULE_PATH, modulePath);
|
||||
}
|
||||
|
||||
appendToFile(file, TEMPLATE_GRADLE_SETTINGS_MERGE, attributes);
|
||||
}
|
||||
return file;
|
||||
}
|
||||
|
||||
private static void saveFile(@NotNull VirtualFile file, @NotNull String templateName, @Nullable Map templateAttributes)
|
||||
throws ConfigurationException {
|
||||
FileTemplateManager manager = FileTemplateManager.getDefaultInstance();
|
||||
FileTemplate template = manager.getInternalTemplate(templateName);
|
||||
try {
|
||||
appendToFile(file, templateAttributes != null ? template.getText(templateAttributes) : template.getText());
|
||||
}
|
||||
catch (IOException e) {
|
||||
LOG.warn(String.format("Unexpected exception on applying template %s config", GradleConstants.SYSTEM_ID.getReadableName()), e);
|
||||
throw new ConfigurationException(
|
||||
e.getMessage(), String.format("Can't apply %s template config text", GradleConstants.SYSTEM_ID.getReadableName())
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private static void appendToFile(@NotNull VirtualFile file, @NotNull String templateName, @Nullable Map templateAttributes)
|
||||
throws ConfigurationException {
|
||||
FileTemplateManager manager = FileTemplateManager.getDefaultInstance();
|
||||
FileTemplate template = manager.getInternalTemplate(templateName);
|
||||
try {
|
||||
appendToFile(file, templateAttributes != null ? template.getText(templateAttributes) : template.getText());
|
||||
}
|
||||
catch (IOException e) {
|
||||
LOG.warn(String.format("Unexpected exception on appending template %s config", GradleConstants.SYSTEM_ID.getReadableName()), e);
|
||||
throw new ConfigurationException(
|
||||
e.getMessage(), String.format("Can't append %s template config text", GradleConstants.SYSTEM_ID.getReadableName())
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Nullable
|
||||
private static VirtualFile getOrCreateExternalProjectConfigFile(@NotNull String parent, @NotNull String fileName) {
|
||||
File file = new File(parent, fileName);
|
||||
FileUtilRt.createIfNotExists(file);
|
||||
return LocalFileSystem.getInstance().refreshAndFindFileByIoFile(file);
|
||||
}
|
||||
|
||||
public void setParentProject(@Nullable ProjectData parentProject) {
|
||||
myParentProject = parentProject;
|
||||
}
|
||||
|
||||
public boolean isInheritGroupId() {
|
||||
return myInheritGroupId;
|
||||
}
|
||||
|
||||
public void setInheritGroupId(boolean inheritGroupId) {
|
||||
myInheritGroupId = inheritGroupId;
|
||||
}
|
||||
|
||||
public boolean isInheritVersion() {
|
||||
return myInheritVersion;
|
||||
}
|
||||
|
||||
public void setInheritVersion(boolean inheritVersion) {
|
||||
myInheritVersion = inheritVersion;
|
||||
}
|
||||
|
||||
public ProjectId getProjectId() {
|
||||
return myProjectId;
|
||||
}
|
||||
|
||||
public void setProjectId(@NotNull ProjectId projectId) {
|
||||
myProjectId = projectId;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public ModuleWizardStep modifySettingsStep(@NotNull SettingsStep settingsStep) {
|
||||
if (settingsStep instanceof ProjectSettingsStep) {
|
||||
final ProjectSettingsStep projectSettingsStep = (ProjectSettingsStep)settingsStep;
|
||||
if (myProjectId != null) {
|
||||
final JTextField moduleNameField = settingsStep.getModuleNameField();
|
||||
if (moduleNameField != null) {
|
||||
moduleNameField.setText(myProjectId.getArtifactId());
|
||||
}
|
||||
projectSettingsStep.setModuleName(myProjectId.getArtifactId());
|
||||
}
|
||||
projectSettingsStep.bindModuleSettings();
|
||||
}
|
||||
return super.modifySettingsStep(settingsStep);
|
||||
}
|
||||
|
||||
public static void appendToFile(@NotNull VirtualFile file, @NotNull String text) throws IOException {
|
||||
String lineSeparator = lineSeparator(file);
|
||||
final String existingText = StringUtil.trimTrailing(VfsUtilCore.loadText(file));
|
||||
String content = (StringUtil.isNotEmpty(existingText) ? existingText + lineSeparator : "") +
|
||||
StringUtil.convertLineSeparators(text, lineSeparator);
|
||||
VfsUtil.saveText(file, content);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static String lineSeparator(@NotNull VirtualFile file) {
|
||||
String lineSeparator = LoadTextUtil.detectLineSeparator(file, true);
|
||||
if (lineSeparator == null) {
|
||||
lineSeparator = CodeStyleSettingsManager.getSettings(ProjectManagerEx.getInstanceEx().getDefaultProject()).getLineSeparator();
|
||||
}
|
||||
return lineSeparator;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static BuildScriptDataBuilder getBuildScriptData(@Nullable Module module) {
|
||||
return module == null ? null : module.getUserData(BUILD_SCRIPT_DATA);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Project createProject(String name, String path) {
|
||||
Project project = super.createProject(name, path);
|
||||
if (project != null) {
|
||||
GradleProjectSettings settings = getExternalProjectSettings();
|
||||
ExternalProjectsManagerImpl.getInstance(project).setStoreExternally(settings.isStoreProjectFilesExternally());
|
||||
}
|
||||
return project;
|
||||
}
|
||||
}
|
||||
-105
@@ -1,105 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="org.jetbrains.kotlin.gradle.kdsl.GradleModuleWizardStep">
|
||||
<grid id="27dc6" binding="myMainPanel" layout-manager="GridLayoutManager" row-count="5" column-count="3" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
|
||||
<margin top="0" left="0" bottom="0" right="0"/>
|
||||
<constraints>
|
||||
<xy x="20" y="20" width="529" height="386"/>
|
||||
</constraints>
|
||||
<properties/>
|
||||
<border type="none"/>
|
||||
<children>
|
||||
<component id="ddae6" class="javax.swing.JLabel">
|
||||
<constraints>
|
||||
<grid row="1" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
|
||||
</constraints>
|
||||
<properties>
|
||||
<labelFor value="d7d25"/>
|
||||
<text value="GroupId"/>
|
||||
</properties>
|
||||
</component>
|
||||
<component id="bbb74" class="javax.swing.JLabel">
|
||||
<constraints>
|
||||
<grid row="2" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
|
||||
</constraints>
|
||||
<properties>
|
||||
<labelFor value="efb5e"/>
|
||||
<text value="ArtifactId"/>
|
||||
</properties>
|
||||
</component>
|
||||
<component id="4eb56" class="javax.swing.JLabel">
|
||||
<constraints>
|
||||
<grid row="3" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
|
||||
</constraints>
|
||||
<properties>
|
||||
<labelFor value="b1344"/>
|
||||
<text value="Version"/>
|
||||
</properties>
|
||||
</component>
|
||||
<component id="d7d25" class="javax.swing.JTextField" binding="myGroupIdField">
|
||||
<constraints>
|
||||
<grid row="1" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="8" fill="1" indent="0" use-parent-layout="false">
|
||||
<preferred-size width="150" height="-1"/>
|
||||
</grid>
|
||||
</constraints>
|
||||
<properties/>
|
||||
</component>
|
||||
<component id="efb5e" class="javax.swing.JTextField" binding="myArtifactIdField">
|
||||
<constraints>
|
||||
<grid row="2" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="8" fill="1" indent="0" use-parent-layout="false">
|
||||
<preferred-size width="150" height="-1"/>
|
||||
</grid>
|
||||
</constraints>
|
||||
<properties/>
|
||||
</component>
|
||||
<component id="b1344" class="javax.swing.JTextField" binding="myVersionField">
|
||||
<constraints>
|
||||
<grid row="3" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="8" fill="1" indent="0" use-parent-layout="false">
|
||||
<preferred-size width="150" height="-1"/>
|
||||
</grid>
|
||||
</constraints>
|
||||
<properties/>
|
||||
</component>
|
||||
<component id="d0095" class="javax.swing.JCheckBox" binding="myInheritGroupIdCheckBox">
|
||||
<constraints>
|
||||
<grid row="1" column="2" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
|
||||
</constraints>
|
||||
<properties>
|
||||
<text value="Inherit"/>
|
||||
</properties>
|
||||
</component>
|
||||
<component id="897ff" class="javax.swing.JCheckBox" binding="myInheritVersionCheckBox">
|
||||
<constraints>
|
||||
<grid row="3" column="2" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
|
||||
</constraints>
|
||||
<properties>
|
||||
<text value="Inherit"/>
|
||||
</properties>
|
||||
</component>
|
||||
<grid id="102d8" binding="myAddToPanel" layout-manager="BorderLayout" hgap="0" vgap="0">
|
||||
<constraints>
|
||||
<grid row="0" column="0" row-span="1" col-span="3" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="true"/>
|
||||
</constraints>
|
||||
<properties/>
|
||||
<border type="none"/>
|
||||
<children/>
|
||||
</grid>
|
||||
<grid id="90185" layout-manager="GridLayoutManager" row-count="1" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
|
||||
<margin top="0" left="0" bottom="0" right="0"/>
|
||||
<constraints>
|
||||
<grid row="4" column="0" row-span="1" col-span="3" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
|
||||
</constraints>
|
||||
<properties>
|
||||
<enabled value="true"/>
|
||||
</properties>
|
||||
<border type="none"/>
|
||||
<children>
|
||||
<vspacer id="f04b8">
|
||||
<constraints>
|
||||
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="6" hsize-policy="1" anchor="0" fill="2" indent="0" use-parent-layout="false"/>
|
||||
</constraints>
|
||||
</vspacer>
|
||||
</children>
|
||||
</grid>
|
||||
</children>
|
||||
</grid>
|
||||
</form>
|
||||
-257
@@ -1,257 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jetbrains.kotlin.gradle.kdsl;
|
||||
|
||||
import com.intellij.ide.util.PropertiesComponent;
|
||||
import com.intellij.ide.util.projectWizard.ModuleWizardStep;
|
||||
import com.intellij.ide.util.projectWizard.WizardContext;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.externalSystem.model.project.ProjectData;
|
||||
import com.intellij.openapi.externalSystem.model.project.ProjectId;
|
||||
import com.intellij.openapi.externalSystem.service.project.wizard.ExternalModuleSettingsStep;
|
||||
import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil;
|
||||
import com.intellij.openapi.module.Module;
|
||||
import com.intellij.openapi.options.ConfigurationException;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.Disposer;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.openapi.wm.IdeFocusManager;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.plugins.gradle.service.project.wizard.GradleParentProjectForm;
|
||||
import org.jetbrains.plugins.gradle.util.GradleConstants;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
|
||||
public class GradleModuleWizardStep extends ModuleWizardStep {
|
||||
private static final Icon WIZARD_ICON = null;
|
||||
|
||||
private static final String INHERIT_GROUP_ID_KEY = "GradleModuleWizard.inheritGroupId";
|
||||
private static final String INHERIT_VERSION_KEY = "GradleModuleWizard.inheritVersion";
|
||||
private static final String DEFAULT_VERSION = "1.0-SNAPSHOT";
|
||||
|
||||
@Nullable
|
||||
private final Project myProjectOrNull;
|
||||
@NotNull
|
||||
private final GradleModuleBuilder myBuilder;
|
||||
@NotNull
|
||||
private final WizardContext myContext;
|
||||
@NotNull
|
||||
private final GradleParentProjectForm myParentProjectForm;
|
||||
|
||||
private String myInheritedGroupId;
|
||||
private String myInheritedVersion;
|
||||
|
||||
private JPanel myMainPanel;
|
||||
|
||||
private JTextField myGroupIdField;
|
||||
private JCheckBox myInheritGroupIdCheckBox;
|
||||
private JTextField myArtifactIdField;
|
||||
private JTextField myVersionField;
|
||||
private JCheckBox myInheritVersionCheckBox;
|
||||
private JPanel myAddToPanel;
|
||||
|
||||
public GradleModuleWizardStep(@NotNull GradleModuleBuilder builder, @NotNull WizardContext context) {
|
||||
myProjectOrNull = context.getProject();
|
||||
myBuilder = builder;
|
||||
myContext = context;
|
||||
myParentProjectForm = new GradleParentProjectForm(context, parentProject -> updateComponents());
|
||||
initComponents();
|
||||
loadSettings();
|
||||
}
|
||||
|
||||
private void initComponents() {
|
||||
myAddToPanel.add(myParentProjectForm.getComponent());
|
||||
ActionListener updatingListener = new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
updateComponents();
|
||||
}
|
||||
};
|
||||
myInheritGroupIdCheckBox.addActionListener(updatingListener);
|
||||
myInheritVersionCheckBox.addActionListener(updatingListener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public JComponent getPreferredFocusedComponent() {
|
||||
return myGroupIdField;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStepLeaving() {
|
||||
saveSettings();
|
||||
}
|
||||
|
||||
private void loadSettings() {
|
||||
myBuilder.setInheritGroupId(getSavedValue(INHERIT_GROUP_ID_KEY, true));
|
||||
myBuilder.setInheritVersion(getSavedValue(INHERIT_VERSION_KEY, true));
|
||||
}
|
||||
|
||||
private void saveSettings() {
|
||||
saveValue(INHERIT_GROUP_ID_KEY, myInheritGroupIdCheckBox.isSelected());
|
||||
saveValue(INHERIT_VERSION_KEY, myInheritVersionCheckBox.isSelected());
|
||||
}
|
||||
|
||||
private static boolean getSavedValue(String key, boolean defaultValue) {
|
||||
return getSavedValue(key, String.valueOf(defaultValue)).equals(String.valueOf(true));
|
||||
}
|
||||
|
||||
private static String getSavedValue(String key, String defaultValue) {
|
||||
String value = PropertiesComponent.getInstance().getValue(key);
|
||||
return value == null ? defaultValue : value;
|
||||
}
|
||||
|
||||
private static void saveValue(String key, boolean value) {
|
||||
saveValue(key, String.valueOf(value));
|
||||
}
|
||||
|
||||
private static void saveValue(String key, String value) {
|
||||
PropertiesComponent.getInstance().setValue(key, value);
|
||||
}
|
||||
|
||||
public JComponent getComponent() {
|
||||
return myMainPanel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean validate() throws ConfigurationException {
|
||||
if (StringUtil.isEmptyOrSpaces(myArtifactIdField.getText())) {
|
||||
ApplicationManager.getApplication().invokeLater(
|
||||
() -> IdeFocusManager.getInstance(myProjectOrNull).requestFocus(myArtifactIdField, true));
|
||||
throw new ConfigurationException("Please, specify artifactId");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateStep() {
|
||||
ProjectData parentProject = myParentProjectForm.getParentProject();
|
||||
ProjectId projectId = myBuilder.getProjectId();
|
||||
|
||||
if (projectId == null) {
|
||||
setTestIfEmpty(myArtifactIdField, myBuilder.getName());
|
||||
setTestIfEmpty(myGroupIdField, parentProject == null ? myBuilder.getName() : parentProject.getGroup());
|
||||
setTestIfEmpty(myVersionField, parentProject == null ? DEFAULT_VERSION : parentProject.getVersion());
|
||||
}
|
||||
else {
|
||||
setTestIfEmpty(myArtifactIdField, projectId.getArtifactId());
|
||||
setTestIfEmpty(myGroupIdField, projectId.getGroupId());
|
||||
setTestIfEmpty(myVersionField, projectId.getVersion());
|
||||
}
|
||||
|
||||
myInheritGroupIdCheckBox.setSelected(myBuilder.isInheritGroupId());
|
||||
myInheritVersionCheckBox.setSelected(myBuilder.isInheritVersion());
|
||||
|
||||
updateComponents();
|
||||
}
|
||||
|
||||
|
||||
private void updateComponents() {
|
||||
final boolean isAddToVisible = myParentProjectForm.isVisible();
|
||||
|
||||
myInheritGroupIdCheckBox.setVisible(isAddToVisible);
|
||||
myInheritVersionCheckBox.setVisible(isAddToVisible);
|
||||
|
||||
myParentProjectForm.updateComponents();
|
||||
ProjectData parentProject = myParentProjectForm.getParentProject();
|
||||
if (parentProject == null) {
|
||||
myContext.putUserData(ExternalModuleSettingsStep.SKIP_STEP_KEY, Boolean.FALSE);
|
||||
myGroupIdField.setEnabled(true);
|
||||
myVersionField.setEnabled(true);
|
||||
myInheritGroupIdCheckBox.setEnabled(false);
|
||||
myInheritVersionCheckBox.setEnabled(false);
|
||||
|
||||
setTestIfEmpty(myArtifactIdField, myBuilder.getName());
|
||||
setTestIfEmpty(myGroupIdField, "");
|
||||
setTestIfEmpty(myVersionField, DEFAULT_VERSION);
|
||||
}
|
||||
else {
|
||||
myContext.putUserData(ExternalModuleSettingsStep.SKIP_STEP_KEY, Boolean.TRUE);
|
||||
myGroupIdField.setEnabled(!myInheritGroupIdCheckBox.isSelected());
|
||||
myVersionField.setEnabled(!myInheritVersionCheckBox.isSelected());
|
||||
|
||||
if (myInheritGroupIdCheckBox.isSelected()
|
||||
|| myGroupIdField.getText().equals(myInheritedGroupId)) {
|
||||
myGroupIdField.setText(parentProject.getGroup());
|
||||
}
|
||||
if (myInheritVersionCheckBox.isSelected()
|
||||
|| myVersionField.getText().equals(myInheritedVersion)) {
|
||||
myVersionField.setText(parentProject.getVersion());
|
||||
}
|
||||
myInheritedGroupId = myGroupIdField.getText();
|
||||
myInheritedVersion = myVersionField.getText();
|
||||
|
||||
myInheritGroupIdCheckBox.setEnabled(true);
|
||||
myInheritVersionCheckBox.setEnabled(true);
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isGradleModuleExist(WizardContext myContext) {
|
||||
for (Module module : myContext.getModulesProvider().getModules()) {
|
||||
if (ExternalSystemApiUtil.isExternalSystemAwareModule(GradleConstants.SYSTEM_ID, module)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateDataModel() {
|
||||
myContext.setProjectBuilder(myBuilder);
|
||||
ProjectData parentProject = myParentProjectForm.getParentProject();
|
||||
myBuilder.setParentProject(parentProject);
|
||||
|
||||
myBuilder.setProjectId(new ProjectId(myGroupIdField.getText(),
|
||||
myArtifactIdField.getText(),
|
||||
myVersionField.getText()));
|
||||
myBuilder.setInheritGroupId(myInheritGroupIdCheckBox.isSelected());
|
||||
myBuilder.setInheritVersion(myInheritVersionCheckBox.isSelected());
|
||||
|
||||
if (StringUtil.isNotEmpty(myBuilder.getProjectId().getArtifactId())) {
|
||||
myContext.setProjectName(myBuilder.getProjectId().getArtifactId());
|
||||
}
|
||||
if (parentProject != null) {
|
||||
myContext.setProjectFileDirectory(parentProject.getLinkedExternalProjectPath() + '/' + myContext.getProjectName());
|
||||
}
|
||||
else {
|
||||
if (myProjectOrNull != null) {
|
||||
myContext.setProjectFileDirectory(myProjectOrNull.getBaseDir().getPath() + '/' + myContext.getProjectName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Icon getIcon() {
|
||||
return WIZARD_ICON;
|
||||
}
|
||||
|
||||
private static void setTestIfEmpty(@NotNull JTextField field, @Nullable String text) {
|
||||
if (StringUtil.isEmpty(field.getText())) {
|
||||
field.setText(StringUtil.notNullize(text));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHelpId() {
|
||||
return "Gradle_Archetype_Dialog";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disposeUIResources() {
|
||||
Disposer.dispose(myParentProjectForm);
|
||||
}
|
||||
}
|
||||
|
||||
-158
@@ -1,158 +0,0 @@
|
||||
/*
|
||||
* Copyright 2000-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jetbrains.kotlin.gradle.kdsl.frameworkSupport;
|
||||
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.util.Function;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
public class BuildScriptDataBuilder {
|
||||
@NotNull private final VirtualFile myBuildScriptFile;
|
||||
private final Set<String> plugins = ContainerUtil.newTreeSet();
|
||||
private final Set<String> pluginsInGroup = ContainerUtil.newTreeSet();
|
||||
private final Set<String> repositories = ContainerUtil.newTreeSet();
|
||||
private final Set<String> dependencies = ContainerUtil.newTreeSet();
|
||||
private final Set<String> properties = ContainerUtil.newTreeSet();
|
||||
private final Set<String> buildScriptProperties = ContainerUtil.newTreeSet();
|
||||
private final Set<String> buildScriptRepositories = ContainerUtil.newTreeSet();
|
||||
private final Set<String> buildScriptDependencies = ContainerUtil.newTreeSet();
|
||||
private final Set<String> other = ContainerUtil.newTreeSet();
|
||||
|
||||
public BuildScriptDataBuilder(@NotNull VirtualFile buildScriptFile) {
|
||||
myBuildScriptFile = buildScriptFile;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public VirtualFile getBuildScriptFile() {
|
||||
return myBuildScriptFile;
|
||||
}
|
||||
|
||||
public String buildConfigurationPart() {
|
||||
List<String> lines = ContainerUtil.newArrayList();
|
||||
addBuildscriptLines(lines, BuildScriptDataBuilder::padding);
|
||||
if (!pluginsInGroup.isEmpty()) {
|
||||
lines.add("plugins {");
|
||||
lines.addAll(ContainerUtil.map(pluginsInGroup, BuildScriptDataBuilder::padding));
|
||||
lines.add("}");
|
||||
lines.add("");
|
||||
}
|
||||
return StringUtil.join(lines, "\n");
|
||||
}
|
||||
|
||||
public String buildMainPart() {
|
||||
List<String> lines = ContainerUtil.newArrayList();
|
||||
if (!plugins.isEmpty()) {
|
||||
lines.addAll(plugins);
|
||||
lines.add("");
|
||||
}
|
||||
if (!properties.isEmpty()) {
|
||||
lines.addAll(properties);
|
||||
lines.add("");
|
||||
}
|
||||
if (!repositories.isEmpty()) {
|
||||
lines.add("repositories {");
|
||||
lines.addAll(ContainerUtil.map(repositories, BuildScriptDataBuilder::padding));
|
||||
lines.add("}");
|
||||
lines.add("");
|
||||
}
|
||||
if (!dependencies.isEmpty()) {
|
||||
lines.add("dependencies {");
|
||||
lines.addAll(ContainerUtil.map(dependencies, BuildScriptDataBuilder::padding));
|
||||
lines.add("}");
|
||||
lines.add("");
|
||||
}
|
||||
if (!other.isEmpty()) {
|
||||
lines.addAll(other);
|
||||
}
|
||||
return StringUtil.join(lines, "\n");
|
||||
}
|
||||
|
||||
private void addBuildscriptLines(@NotNull List<String> lines, @NotNull Function<String, String> padding) {
|
||||
if (!buildScriptRepositories.isEmpty() || !buildScriptDependencies.isEmpty() || !buildScriptProperties.isEmpty()) {
|
||||
lines.add("buildscript {");
|
||||
final List<String> buildScriptLines = ContainerUtil.newSmartList();
|
||||
if (!buildScriptProperties.isEmpty()) {
|
||||
buildScriptLines.addAll(buildScriptProperties);
|
||||
buildScriptLines.add("");
|
||||
}
|
||||
if (!buildScriptRepositories.isEmpty()) {
|
||||
buildScriptLines.add("repositories {");
|
||||
buildScriptLines.addAll(ContainerUtil.map(buildScriptRepositories, padding));
|
||||
buildScriptLines.add("}");
|
||||
}
|
||||
if (!buildScriptDependencies.isEmpty()) {
|
||||
buildScriptLines.add("dependencies {");
|
||||
buildScriptLines.addAll(ContainerUtil.map(buildScriptDependencies, padding));
|
||||
buildScriptLines.add("}");
|
||||
}
|
||||
lines.addAll(ContainerUtil.map(buildScriptLines, padding));
|
||||
lines.add("}");
|
||||
lines.add("");
|
||||
}
|
||||
}
|
||||
|
||||
public BuildScriptDataBuilder addBuildscriptPropertyDefinition(@NotNull String definition) {
|
||||
buildScriptProperties.add(definition.trim());
|
||||
return this;
|
||||
}
|
||||
|
||||
public BuildScriptDataBuilder addBuildscriptRepositoriesDefinition(@NotNull String definition) {
|
||||
buildScriptRepositories.add(definition.trim());
|
||||
return this;
|
||||
}
|
||||
|
||||
public BuildScriptDataBuilder addBuildscriptDependencyNotation(@NotNull String notation) {
|
||||
buildScriptDependencies.add(notation.trim());
|
||||
return this;
|
||||
}
|
||||
|
||||
public BuildScriptDataBuilder addPluginDefinitionInPluginsGroup(@NotNull String definition) {
|
||||
pluginsInGroup.add(definition.trim());
|
||||
return this;
|
||||
}
|
||||
|
||||
public BuildScriptDataBuilder addPluginDefinition(@NotNull String definition) {
|
||||
plugins.add(definition.trim());
|
||||
return this;
|
||||
}
|
||||
|
||||
public BuildScriptDataBuilder addRepositoriesDefinition(@NotNull String definition) {
|
||||
repositories.add(definition.trim());
|
||||
return this;
|
||||
}
|
||||
|
||||
public BuildScriptDataBuilder addDependencyNotation(@NotNull String notation) {
|
||||
dependencies.add(notation.trim());
|
||||
return this;
|
||||
}
|
||||
|
||||
public BuildScriptDataBuilder addPropertyDefinition(@NotNull String definition) {
|
||||
properties.add(definition.trim());
|
||||
return this;
|
||||
}
|
||||
|
||||
public BuildScriptDataBuilder addOther(@NotNull String definition) {
|
||||
other.add(definition.trim());
|
||||
return this;
|
||||
}
|
||||
|
||||
private static String padding(String s) {return StringUtil.isNotEmpty(s) ? " " + s : "";}
|
||||
}
|
||||
-72
@@ -1,72 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jetbrains.kotlin.gradle.kdsl.frameworkSupport;
|
||||
|
||||
import com.intellij.framework.addSupport.FrameworkSupportInModuleConfigurable;
|
||||
import com.intellij.framework.addSupport.FrameworkSupportInModuleProvider;
|
||||
import com.intellij.ide.util.frameworkSupport.FrameworkSupportModel;
|
||||
import com.intellij.openapi.extensions.ExtensionPointName;
|
||||
import com.intellij.openapi.module.Module;
|
||||
import com.intellij.openapi.module.ModuleType;
|
||||
import com.intellij.openapi.roots.ModifiableModelsProvider;
|
||||
import com.intellij.openapi.roots.ModifiableRootModel;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
import static org.jetbrains.kotlin.gradle.kdsl.GradleModuleBuilder.getBuildScriptData;
|
||||
|
||||
public abstract class GradleFrameworkSupportProvider extends FrameworkSupportInModuleProvider {
|
||||
|
||||
public static final ExtensionPointName<GradleFrameworkSupportProvider> EP_NAME =
|
||||
ExtensionPointName.create("org.jetbrains.kotlin.gradleFrameworkSupport");
|
||||
|
||||
public abstract void addSupport(@NotNull Module module, @NotNull ModifiableRootModel rootModel,
|
||||
@NotNull ModifiableModelsProvider modifiableModelsProvider,
|
||||
@NotNull BuildScriptDataBuilder buildScriptData);
|
||||
|
||||
public JComponent createComponent() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public FrameworkSupportInModuleConfigurable createConfigurable(@NotNull FrameworkSupportModel model) {
|
||||
return new FrameworkSupportInModuleConfigurable() {
|
||||
@Nullable
|
||||
@Override
|
||||
public JComponent createComponent() {
|
||||
return GradleFrameworkSupportProvider.this.createComponent();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addSupport(@NotNull Module module,
|
||||
@NotNull ModifiableRootModel rootModel,
|
||||
@NotNull ModifiableModelsProvider modifiableModelsProvider) {
|
||||
final BuildScriptDataBuilder buildScriptData = getBuildScriptData(module);
|
||||
if (buildScriptData != null) {
|
||||
GradleFrameworkSupportProvider.this.addSupport(module, rootModel, modifiableModelsProvider, buildScriptData);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabledForModuleType(@NotNull ModuleType moduleType) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
-67
@@ -1,67 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jetbrains.kotlin.gradle.kdsl.frameworkSupport;
|
||||
|
||||
import com.intellij.framework.FrameworkTypeEx;
|
||||
import com.intellij.framework.addSupport.FrameworkSupportInModuleProvider;
|
||||
import com.intellij.openapi.module.Module;
|
||||
import com.intellij.openapi.roots.ModifiableModelsProvider;
|
||||
import com.intellij.openapi.roots.ModifiableRootModel;
|
||||
import icons.JetgroovyIcons;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
public class GradleGroovyFrameworkSupportProvider extends GradleFrameworkSupportProvider {
|
||||
|
||||
public static final String ID = "groovy";
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public FrameworkTypeEx getFrameworkType() {
|
||||
return new FrameworkTypeEx(ID) {
|
||||
@NotNull
|
||||
@Override
|
||||
public FrameworkSupportInModuleProvider createProvider() {
|
||||
return GradleGroovyFrameworkSupportProvider.this;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String getPresentableName() {
|
||||
return "Groovy";
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Icon getIcon() {
|
||||
return JetgroovyIcons.Groovy.Groovy_16x16;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addSupport(@NotNull Module module,
|
||||
@NotNull ModifiableRootModel rootModel,
|
||||
@NotNull ModifiableModelsProvider modifiableModelsProvider,
|
||||
@NotNull BuildScriptDataBuilder buildScriptData) {
|
||||
buildScriptData
|
||||
.addPluginDefinition("plugin(\"groovy\")")
|
||||
.addRepositoriesDefinition("mavenCentral()")
|
||||
.addDependencyNotation("compile(\"org.codehaus.groovy:groovy-all:2.3.11\")")
|
||||
.addDependencyNotation("testCompile(\"junit\", \"junit\", \"4.12\")");
|
||||
}
|
||||
}
|
||||
-68
@@ -1,68 +0,0 @@
|
||||
/*
|
||||
* Copyright 2000-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jetbrains.kotlin.gradle.kdsl.frameworkSupport;
|
||||
|
||||
import com.intellij.framework.FrameworkTypeEx;
|
||||
import com.intellij.framework.addSupport.FrameworkSupportInModuleProvider;
|
||||
import com.intellij.icons.AllIcons;
|
||||
import com.intellij.openapi.module.Module;
|
||||
import com.intellij.openapi.roots.ModifiableModelsProvider;
|
||||
import com.intellij.openapi.roots.ModifiableRootModel;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
public class GradleJavaFrameworkSupportProvider extends GradleFrameworkSupportProvider {
|
||||
|
||||
public static final String ID = "java";
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public FrameworkTypeEx getFrameworkType() {
|
||||
return new FrameworkTypeEx(ID) {
|
||||
@NotNull
|
||||
@Override
|
||||
public FrameworkSupportInModuleProvider createProvider() {
|
||||
return GradleJavaFrameworkSupportProvider.this;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String getPresentableName() {
|
||||
return "Java";
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Icon getIcon() {
|
||||
return AllIcons.Nodes.Module;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addSupport(@NotNull Module module,
|
||||
@NotNull ModifiableRootModel rootModel,
|
||||
@NotNull ModifiableModelsProvider modifiableModelsProvider,
|
||||
@NotNull BuildScriptDataBuilder buildScriptData) {
|
||||
buildScriptData
|
||||
.addPluginDefinition("plugin(\"java\")")
|
||||
// TODO: in gradle > 4.0 it is just 'java { ... }'
|
||||
.addOther("configure<JavaPluginConvention> {\n sourceCompatibility = JavaVersion.VERSION_1_8\n}")
|
||||
.addRepositoriesDefinition("mavenCentral()")
|
||||
.addDependencyNotation("testCompile(\"junit\", \"junit\", \"4.12\")");
|
||||
}
|
||||
}
|
||||
-115
@@ -1,115 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.gradle.kdsl.frameworkSupport
|
||||
|
||||
import com.intellij.framework.FrameworkTypeEx
|
||||
import com.intellij.framework.addSupport.FrameworkSupportInModuleProvider
|
||||
import com.intellij.openapi.module.Module
|
||||
import com.intellij.openapi.roots.ModifiableModelsProvider
|
||||
import com.intellij.openapi.roots.ModifiableRootModel
|
||||
import com.intellij.openapi.vfs.VfsUtil
|
||||
import org.jetbrains.kotlin.idea.KotlinIcons
|
||||
import org.jetbrains.kotlin.idea.configuration.*
|
||||
import org.jetbrains.kotlin.idea.configuration.KotlinBuildScriptManipulator.Companion.GSK_KOTLIN_VERSION_PROPERTY_NAME
|
||||
import org.jetbrains.kotlin.idea.configuration.KotlinBuildScriptManipulator.Companion.getCompileDependencySnippet
|
||||
import org.jetbrains.kotlin.idea.configuration.KotlinBuildScriptManipulator.Companion.getKotlinGradlePluginClassPathSnippet
|
||||
import org.jetbrains.kotlin.idea.versions.*
|
||||
import javax.swing.Icon
|
||||
|
||||
abstract class GradleKotlinDSLKotlinFrameworkSupportProvider(
|
||||
val frameworkTypeId: String,
|
||||
val displayName: String,
|
||||
val frameworkIcon: Icon
|
||||
) : GradleFrameworkSupportProvider() {
|
||||
override fun getFrameworkType(): FrameworkTypeEx = object : FrameworkTypeEx(frameworkTypeId) {
|
||||
override fun getIcon(): Icon = frameworkIcon
|
||||
override fun getPresentableName(): String = displayName
|
||||
override fun createProvider(): FrameworkSupportInModuleProvider = this@GradleKotlinDSLKotlinFrameworkSupportProvider
|
||||
}
|
||||
|
||||
override fun addSupport(
|
||||
module: Module,
|
||||
rootModel: ModifiableRootModel,
|
||||
modifiableModelsProvider: ModifiableModelsProvider,
|
||||
buildScriptData: BuildScriptDataBuilder
|
||||
) {
|
||||
var kotlinVersion = bundledRuntimeVersion()
|
||||
val additionalRepository = getRepositoryForVersion(kotlinVersion)
|
||||
if (isSnapshot(bundledRuntimeVersion())) {
|
||||
kotlinVersion = LAST_SNAPSHOT_VERSION
|
||||
}
|
||||
|
||||
if (additionalRepository != null) {
|
||||
val repository = additionalRepository.toKotlinRepositorySnippet()
|
||||
buildScriptData.addBuildscriptRepositoriesDefinition(repository)
|
||||
buildScriptData.addRepositoriesDefinition("mavenCentral()")
|
||||
buildScriptData.addRepositoriesDefinition(repository)
|
||||
}
|
||||
|
||||
buildScriptData
|
||||
.addPropertyDefinition("val $GSK_KOTLIN_VERSION_PROPERTY_NAME: String by extra")
|
||||
.addPluginDefinition(getPluginDefinition())
|
||||
.addBuildscriptRepositoriesDefinition("mavenCentral()")
|
||||
.addRepositoriesDefinition("mavenCentral()")
|
||||
// TODO: in gradle > 4.1 this could be single declaration e.g. 'val kotlin_version: String by extra { "1.1.11" }'
|
||||
.addBuildscriptPropertyDefinition("var $GSK_KOTLIN_VERSION_PROPERTY_NAME: String by extra\n$GSK_KOTLIN_VERSION_PROPERTY_NAME = \"$kotlinVersion\"")
|
||||
.addDependencyNotation(getRuntimeLibrary(rootModel))
|
||||
.addBuildscriptDependencyNotation(getKotlinGradlePluginClassPathSnippet())
|
||||
}
|
||||
|
||||
protected abstract fun getRuntimeLibrary(rootModel: ModifiableRootModel): String
|
||||
|
||||
protected abstract fun getPluginDefinition(): String
|
||||
}
|
||||
|
||||
class GradleKotlinDSLKotlinJavaFrameworkSupportProvider :
|
||||
GradleKotlinDSLKotlinFrameworkSupportProvider("KOTLIN", "Kotlin (Java)", KotlinIcons.SMALL_LOGO) {
|
||||
|
||||
override fun getPluginDefinition() = "plugin(\"${KotlinGradleModuleConfigurator.KOTLIN}\")"
|
||||
|
||||
override fun getRuntimeLibrary(rootModel: ModifiableRootModel) =
|
||||
getCompileDependencySnippet(KOTLIN_GROUP_ID, getStdlibArtifactId(rootModel.sdk, bundledRuntimeVersion()))
|
||||
|
||||
override fun addSupport(
|
||||
module: Module,
|
||||
rootModel: ModifiableRootModel,
|
||||
modifiableModelsProvider: ModifiableModelsProvider,
|
||||
buildScriptData: BuildScriptDataBuilder
|
||||
) {
|
||||
super.addSupport(module, rootModel, modifiableModelsProvider, buildScriptData)
|
||||
val jvmTarget = getDefaultJvmTarget(rootModel.sdk, bundledRuntimeVersion())
|
||||
if (jvmTarget != null) {
|
||||
buildScriptData
|
||||
.addImports("import org.jetbrains.kotlin.gradle.tasks.KotlinCompile")
|
||||
.addOther("tasks.withType<KotlinCompile> {\n kotlinOptions.jvmTarget = \"1.8\"\n}\n")
|
||||
}
|
||||
}
|
||||
|
||||
private fun BuildScriptDataBuilder.addImports(vararg import: String): BuildScriptDataBuilder = apply {
|
||||
val text = VfsUtil.loadText(buildScriptFile)
|
||||
VfsUtil.saveText(buildScriptFile, import.joinToString(separator = "\n") + "\n\n" + text)
|
||||
}
|
||||
}
|
||||
|
||||
class GradleKotlinDSLKotlinJSFrameworkSupportProvider :
|
||||
GradleKotlinDSLKotlinFrameworkSupportProvider("KOTLIN_JS", "Kotlin (JavaScript)", KotlinIcons.JS) {
|
||||
|
||||
override fun getPluginDefinition(): String = "plugin(\"${KotlinJsGradleModuleConfigurator.KOTLIN_JS}\")"
|
||||
|
||||
override fun getRuntimeLibrary(rootModel: ModifiableRootModel) =
|
||||
getCompileDependencySnippet(KOTLIN_GROUP_ID, MAVEN_JS_STDLIB_ID.removePrefix("kotlin-"))
|
||||
}
|
||||
-93
@@ -1,93 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jetbrains.kotlin.gradle.kdsl.frameworkSupport
|
||||
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import com.intellij.util.containers.ContainerUtil
|
||||
|
||||
class KotlinBuildScriptDataBuilder(buildScriptFile: VirtualFile) : BuildScriptDataBuilder(buildScriptFile) {
|
||||
private val plugins: MutableSet<String> = ContainerUtil.newTreeSet<String>()
|
||||
private val properties: MutableSet<String> = ContainerUtil.newTreeSet<String>()
|
||||
private val repositories: MutableSet<String> = ContainerUtil.newTreeSet<String>()
|
||||
private val dependencies: MutableSet<String> = ContainerUtil.newTreeSet<String>()
|
||||
|
||||
private val buildScriptProperties: MutableSet<String> = ContainerUtil.newTreeSet<String>()
|
||||
private val buildScriptRepositories: MutableSet<String> = ContainerUtil.newTreeSet<String>()
|
||||
private val buildScriptDependencies: MutableSet<String> = ContainerUtil.newTreeSet<String>()
|
||||
private val other: MutableSet<String> = ContainerUtil.newTreeSet<String>()
|
||||
|
||||
override fun addPluginDefinition(definition: String): BuildScriptDataBuilder = apply { plugins.add(definition) }
|
||||
|
||||
override fun addRepositoriesDefinition(definition: String): BuildScriptDataBuilder = apply { repositories.add(definition) }
|
||||
|
||||
override fun addPropertyDefinition(definition: String): BuildScriptDataBuilder = apply { properties.add(definition) }
|
||||
|
||||
override fun addDependencyNotation(notation: String): BuildScriptDataBuilder = apply { dependencies.add(notation) }
|
||||
|
||||
override fun addBuildscriptPropertyDefinition(definition: String): BuildScriptDataBuilder = apply { buildScriptProperties.add(definition) }
|
||||
|
||||
override fun addBuildscriptRepositoriesDefinition(definition: String): BuildScriptDataBuilder = apply { buildScriptRepositories.add(definition) }
|
||||
|
||||
override fun addBuildscriptDependencyNotation(notation: String): BuildScriptDataBuilder = apply { buildScriptDependencies.add(notation) }
|
||||
|
||||
override fun addOther(definition: String): BuildScriptDataBuilder = apply { other.add(definition) }
|
||||
|
||||
override fun buildMainPart(): String = buildString {
|
||||
|
||||
appendlnIfNotNull(buildBuildScriptBlock())
|
||||
|
||||
appendlnIfNotNull(buildBlock("apply", plugins))
|
||||
|
||||
if (properties.isNotEmpty()) {
|
||||
properties.forEach { appendln(it) }
|
||||
appendln()
|
||||
}
|
||||
|
||||
appendlnIfNotNull(buildBlock("repositories", repositories))
|
||||
|
||||
appendlnIfNotNull(buildBlock("dependencies", dependencies))
|
||||
|
||||
other.forEach { appendln(it) }
|
||||
}
|
||||
|
||||
private fun buildBuildScriptBlock(): String? = buildString {
|
||||
if (buildScriptProperties.isEmpty() || buildScriptRepositories.isEmpty() || buildScriptDependencies.isEmpty()) {
|
||||
return null
|
||||
}
|
||||
|
||||
appendln("buildscript {")
|
||||
buildScriptProperties.forEach { appendln(it.withMargin) }
|
||||
appendln()
|
||||
appendlnIfNotNull(buildBlock("repositories", buildScriptRepositories)?.withMargin)
|
||||
appendlnIfNotNull(buildBlock("dependencies", buildScriptDependencies)?.withMargin)
|
||||
appendln("}")
|
||||
}
|
||||
|
||||
private fun buildBlock(name: String, lines: Set<String>): String? = buildString {
|
||||
if (lines.isEmpty()) {
|
||||
return null
|
||||
}
|
||||
|
||||
appendln("$name {")
|
||||
lines.forEach { appendln(it.withMargin) }
|
||||
appendln("}")
|
||||
}
|
||||
|
||||
private val String.withMargin: String
|
||||
get() = lines().joinToString(separator = "\n") { " " + it }
|
||||
|
||||
private fun StringBuilder.appendlnIfNotNull(text: String?) = text?.let { appendln(it) }
|
||||
}
|
||||
-70
@@ -1,70 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.configuration
|
||||
|
||||
import com.intellij.openapi.roots.DependencyScope
|
||||
import com.intellij.openapi.roots.ExternalLibraryDescriptor
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.PsiFile
|
||||
import org.gradle.util.GradleVersion
|
||||
import org.jetbrains.plugins.gradle.frameworkSupport.BuildScriptDataBuilder
|
||||
|
||||
interface GradleBuildScriptManipulator<out Psi: PsiFile> {
|
||||
val scriptFile: Psi
|
||||
val preferNewSyntax: Boolean
|
||||
|
||||
fun isConfiguredWithOldSyntax(kotlinPluginName: String): Boolean
|
||||
fun isConfigured(kotlinPluginExpression: String): Boolean
|
||||
|
||||
fun configureModuleBuildScript(
|
||||
kotlinPluginName: String,
|
||||
kotlinPluginExpression: String,
|
||||
stdlibArtifactName: String,
|
||||
version: String,
|
||||
jvmTarget: String?
|
||||
): Boolean
|
||||
|
||||
fun configureProjectBuildScript(kotlinPluginName: String, version: String): Boolean
|
||||
|
||||
fun changeCoroutineConfiguration(coroutineOption: String): PsiElement?
|
||||
|
||||
fun changeLanguageVersion(version: String, forTests: Boolean): PsiElement?
|
||||
|
||||
fun changeApiVersion(version: String, forTests: Boolean): PsiElement?
|
||||
|
||||
fun addKotlinLibraryToModuleBuildScript(
|
||||
scope: DependencyScope,
|
||||
libraryDescriptor: ExternalLibraryDescriptor,
|
||||
isAndroidModule: Boolean
|
||||
)
|
||||
|
||||
fun getKotlinStdlibVersion(): String?
|
||||
|
||||
// For settings.gradle/settings.gradle.kts
|
||||
|
||||
fun addMavenCentralPluginRepository()
|
||||
fun addPluginRepository(repository: RepositoryDescription)
|
||||
|
||||
fun addResolutionStrategy(pluginId: String)
|
||||
}
|
||||
|
||||
val MIN_GRADLE_VERSION_FOR_NEW_PLUGIN_SYNTAX = GradleVersion.version("4.4")
|
||||
|
||||
// Kept for compatibility reasons (pre-181.3 IDEAs)
|
||||
fun GradleBuildScriptManipulator<*>.useNewSyntax(kotlinPluginName: String) = false
|
||||
val BuildScriptDataBuilder.gradleVersion get() = GradleVersion.version("0.0")
|
||||
fun BuildScriptDataBuilder.addPluginDefinitionInPluginsGroup(pluginId: String) {}
|
||||
-240
@@ -1,240 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.configuration
|
||||
|
||||
import com.intellij.framework.FrameworkTypeEx
|
||||
import com.intellij.framework.addSupport.FrameworkSupportInModuleConfigurable
|
||||
import com.intellij.framework.addSupport.FrameworkSupportInModuleProvider
|
||||
import com.intellij.ide.util.frameworkSupport.FrameworkSupportModel
|
||||
import com.intellij.openapi.module.Module
|
||||
import com.intellij.openapi.projectRoots.Sdk
|
||||
import com.intellij.openapi.roots.ModifiableModelsProvider
|
||||
import com.intellij.openapi.roots.ModifiableRootModel
|
||||
import com.intellij.psi.PsiFile
|
||||
import org.jetbrains.kotlin.idea.KotlinIcons
|
||||
import org.jetbrains.kotlin.idea.versions.*
|
||||
import org.jetbrains.plugins.gradle.frameworkSupport.BuildScriptDataBuilder
|
||||
import org.jetbrains.plugins.gradle.frameworkSupport.GradleFrameworkSupportProvider
|
||||
import javax.swing.Icon
|
||||
import javax.swing.JComponent
|
||||
import javax.swing.JLabel
|
||||
|
||||
abstract class GradleKotlinFrameworkSupportProvider(
|
||||
val frameworkTypeId: String,
|
||||
val displayName: String,
|
||||
val frameworkIcon: Icon
|
||||
) : GradleFrameworkSupportProvider() {
|
||||
override fun getFrameworkType(): FrameworkTypeEx = object : FrameworkTypeEx(frameworkTypeId) {
|
||||
override fun getIcon(): Icon = frameworkIcon
|
||||
|
||||
override fun getPresentableName(): String = displayName
|
||||
|
||||
override fun createProvider(): FrameworkSupportInModuleProvider = this@GradleKotlinFrameworkSupportProvider
|
||||
}
|
||||
|
||||
override fun createConfigurable(model: FrameworkSupportModel): FrameworkSupportInModuleConfigurable {
|
||||
val configurable = KotlinGradleFrameworkSupportInModuleConfigurable(model, this)
|
||||
return object : FrameworkSupportInModuleConfigurable() {
|
||||
override fun addSupport(module: Module, rootModel: ModifiableRootModel, modifiableModelsProvider: ModifiableModelsProvider) {
|
||||
configurable.addSupport(module, rootModel, modifiableModelsProvider)
|
||||
}
|
||||
|
||||
override fun createComponent(): JComponent = JLabel(getDescription())
|
||||
}
|
||||
}
|
||||
|
||||
override fun addSupport(
|
||||
module: Module,
|
||||
rootModel: ModifiableRootModel,
|
||||
modifiableModelsProvider: ModifiableModelsProvider,
|
||||
buildScriptData: BuildScriptDataBuilder
|
||||
) {
|
||||
addSupport(buildScriptData, module, rootModel.sdk, true)
|
||||
}
|
||||
|
||||
open fun addSupport(buildScriptData: BuildScriptDataBuilder, module: Module, sdk: Sdk?, specifyPluginVersionIfNeeded: Boolean) {
|
||||
var kotlinVersion = bundledRuntimeVersion()
|
||||
val additionalRepository = getRepositoryForVersion(kotlinVersion)
|
||||
if (isSnapshot(bundledRuntimeVersion())) {
|
||||
kotlinVersion = LAST_SNAPSHOT_VERSION
|
||||
}
|
||||
|
||||
val useNewSyntax = buildScriptData.gradleVersion >= MIN_GRADLE_VERSION_FOR_NEW_PLUGIN_SYNTAX
|
||||
if (useNewSyntax) {
|
||||
if (additionalRepository != null) {
|
||||
val oneLineRepository = additionalRepository.toGroovyRepositorySnippet().replace('\n', ' ')
|
||||
updateSettingsScript(module) {
|
||||
with(KotlinWithGradleConfigurator.getManipulator(it)) {
|
||||
addPluginRepository(additionalRepository)
|
||||
addMavenCentralPluginRepository()
|
||||
addPluginRepository(DEFAULT_GRADLE_PLUGIN_REPOSITORY)
|
||||
}
|
||||
}
|
||||
buildScriptData.addRepositoriesDefinition("mavenCentral()")
|
||||
buildScriptData.addRepositoriesDefinition(oneLineRepository)
|
||||
}
|
||||
|
||||
buildScriptData.addPluginDefinitionInPluginsGroup(
|
||||
getPluginExpression() + if (specifyPluginVersionIfNeeded) " version '$kotlinVersion'" else ""
|
||||
)
|
||||
} else {
|
||||
if (additionalRepository != null) {
|
||||
val oneLineRepository = additionalRepository.toGroovyRepositorySnippet().replace('\n', ' ')
|
||||
buildScriptData.addBuildscriptRepositoriesDefinition(oneLineRepository)
|
||||
|
||||
buildScriptData.addRepositoriesDefinition("mavenCentral()")
|
||||
buildScriptData.addRepositoriesDefinition(oneLineRepository)
|
||||
}
|
||||
|
||||
buildScriptData
|
||||
.addPluginDefinition(KotlinWithGradleConfigurator.getGroovyApplyPluginDirective(getPluginId()))
|
||||
.addBuildscriptRepositoriesDefinition("mavenCentral()")
|
||||
.addBuildscriptPropertyDefinition("ext.kotlin_version = '$kotlinVersion'")
|
||||
}
|
||||
|
||||
buildScriptData.addRepositoriesDefinition("mavenCentral()")
|
||||
|
||||
for (dependency in getDependencies(sdk)) {
|
||||
buildScriptData.addDependencyNotation(KotlinWithGradleConfigurator.getGroovyDependencySnippet(dependency, "compile", !useNewSyntax))
|
||||
}
|
||||
for (dependency in getTestDependencies()) {
|
||||
buildScriptData.addDependencyNotation(
|
||||
if (":" in dependency)
|
||||
"testCompile \"$dependency\""
|
||||
else
|
||||
KotlinWithGradleConfigurator.getGroovyDependencySnippet(dependency, "testCompile", !useNewSyntax)
|
||||
)
|
||||
}
|
||||
|
||||
if (useNewSyntax) {
|
||||
updateSettingsScript(module) { updateSettingsScript(it, specifyPluginVersionIfNeeded) }
|
||||
} else {
|
||||
buildScriptData.addBuildscriptDependencyNotation(KotlinWithGradleConfigurator.CLASSPATH)
|
||||
}
|
||||
}
|
||||
|
||||
protected open fun updateSettingsScript(settingsScript: PsiFile, specifyPluginVersionIfNeeded: Boolean) { }
|
||||
|
||||
protected abstract fun getDependencies(sdk: Sdk?): List<String>
|
||||
protected open fun getTestDependencies(): List<String> = listOf()
|
||||
|
||||
protected abstract fun getPluginId(): String
|
||||
protected abstract fun getPluginExpression(): String
|
||||
|
||||
protected abstract fun getDescription(): String
|
||||
}
|
||||
|
||||
open class GradleKotlinJavaFrameworkSupportProvider(
|
||||
frameworkTypeId: String = "KOTLIN",
|
||||
displayName: String = "Kotlin (Java)"
|
||||
) : GradleKotlinFrameworkSupportProvider(frameworkTypeId, displayName, KotlinIcons.SMALL_LOGO) {
|
||||
|
||||
override fun getPluginId() = KotlinGradleModuleConfigurator.KOTLIN
|
||||
override fun getPluginExpression() = "id 'org.jetbrains.kotlin.jvm'"
|
||||
|
||||
override fun getDependencies(sdk: Sdk?) = listOf(getStdlibArtifactId(sdk, bundledRuntimeVersion()))
|
||||
|
||||
override fun addSupport(buildScriptData: BuildScriptDataBuilder, module: Module, sdk: Sdk?, specifyPluginVersionIfNeeded: Boolean) {
|
||||
super.addSupport(buildScriptData, module, sdk, specifyPluginVersionIfNeeded)
|
||||
val jvmTarget = getDefaultJvmTarget(sdk, bundledRuntimeVersion())
|
||||
if (jvmTarget != null) {
|
||||
val description = jvmTarget.description
|
||||
buildScriptData.addOther("compileKotlin {\n kotlinOptions.jvmTarget = \"$description\"\n}\n\n")
|
||||
buildScriptData.addOther("compileTestKotlin {\n kotlinOptions.jvmTarget = \"$description\"\n}\n")
|
||||
}
|
||||
}
|
||||
|
||||
override fun getDescription() = "A Kotlin library or application targeting the JVM"
|
||||
}
|
||||
|
||||
open class GradleKotlinJSFrameworkSupportProvider(
|
||||
frameworkTypeId: String = "KOTLIN_JS",
|
||||
displayName: String = "Kotlin (JavaScript)"
|
||||
) : GradleKotlinFrameworkSupportProvider(frameworkTypeId, displayName, KotlinIcons.JS) {
|
||||
|
||||
override fun getPluginId() = KotlinJsGradleModuleConfigurator.KOTLIN_JS
|
||||
override fun getPluginExpression() = "id 'kotlin2js'"
|
||||
|
||||
override fun getDependencies(sdk: Sdk?) = listOf(MAVEN_JS_STDLIB_ID)
|
||||
|
||||
override fun getTestDependencies() = listOf(MAVEN_JS_TEST_ID)
|
||||
|
||||
override fun updateSettingsScript(settingsScript: PsiFile, specifyPluginVersionIfNeeded: Boolean) {
|
||||
if (specifyPluginVersionIfNeeded) {
|
||||
KotlinWithGradleConfigurator.getManipulator(settingsScript).addResolutionStrategy("kotlin2js")
|
||||
}
|
||||
}
|
||||
|
||||
override fun getDescription() = "A Kotlin library or application targeting JavaScript"
|
||||
}
|
||||
|
||||
open class GradleKotlinMPPCommonFrameworkSupportProvider :
|
||||
GradleKotlinFrameworkSupportProvider("KOTLIN_MPP_COMMON", "Kotlin (Multiplatform Common - Experimental)", KotlinIcons.MPP) {
|
||||
override fun getPluginId() = "kotlin-platform-common"
|
||||
override fun getPluginExpression() = "id 'kotlin-platform-common'"
|
||||
|
||||
override fun getDependencies(sdk: Sdk?) = listOf(MAVEN_COMMON_STDLIB_ID)
|
||||
override fun getTestDependencies() = listOf(MAVEN_COMMON_TEST_ID, MAVEN_COMMON_TEST_ANNOTATIONS_ID)
|
||||
|
||||
override fun updateSettingsScript(settingsScript: PsiFile, specifyPluginVersionIfNeeded: Boolean) {
|
||||
if (specifyPluginVersionIfNeeded) {
|
||||
KotlinWithGradleConfigurator.getManipulator(settingsScript).addResolutionStrategy("kotlin-platform-common")
|
||||
}
|
||||
}
|
||||
|
||||
override fun getDescription() = "Shared code for a Kotlin multiplatform project (targeting JVM and JS)"
|
||||
}
|
||||
|
||||
class GradleKotlinMPPJavaFrameworkSupportProvider
|
||||
: GradleKotlinJavaFrameworkSupportProvider("KOTLIN_MPP_JVM", "Kotlin (Multiplatform JVM - Experimental)") {
|
||||
|
||||
override fun getPluginId() = "kotlin-platform-jvm"
|
||||
override fun getPluginExpression() = "id 'kotlin-platform-jvm'"
|
||||
|
||||
override fun getDescription() = "JVM-specific code for a Kotlin multiplatform project"
|
||||
override fun getTestDependencies() = listOf(MAVEN_TEST_ID, MAVEN_TEST_JUNIT_ID, "junit:junit:4.12")
|
||||
|
||||
override fun addSupport(buildScriptData: BuildScriptDataBuilder, module: Module, sdk: Sdk?, specifyPluginVersionIfNeeded: Boolean) {
|
||||
super.addSupport(buildScriptData, module, sdk, specifyPluginVersionIfNeeded)
|
||||
val jvmTarget = getDefaultJvmTarget(sdk, bundledRuntimeVersion())
|
||||
if (jvmTarget != null) {
|
||||
val description = jvmTarget.description
|
||||
buildScriptData.addOther("sourceCompatibility = \"$description\"\n\n")
|
||||
}
|
||||
}
|
||||
|
||||
override fun updateSettingsScript(settingsScript: PsiFile, specifyPluginVersionIfNeeded: Boolean) {
|
||||
if (specifyPluginVersionIfNeeded) {
|
||||
KotlinWithGradleConfigurator.getManipulator(settingsScript).addResolutionStrategy("kotlin-platform-jvm")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class GradleKotlinMPPJSFrameworkSupportProvider
|
||||
: GradleKotlinJSFrameworkSupportProvider("KOTLIN_MPP_JS", "Kotlin (Multiplatform JS - Experimental)") {
|
||||
|
||||
override fun getPluginId() = "kotlin-platform-js"
|
||||
override fun getPluginExpression() = "id 'kotlin-platform-js'"
|
||||
|
||||
override fun updateSettingsScript(settingsScript: PsiFile, specifyPluginVersionIfNeeded: Boolean) {
|
||||
if (specifyPluginVersionIfNeeded) {
|
||||
KotlinWithGradleConfigurator.getManipulator(settingsScript).addResolutionStrategy("kotlin-platform-js")
|
||||
}
|
||||
}
|
||||
|
||||
override fun getDescription() = "JavaScript-specific code for a Kotlin multiplatform project"
|
||||
}
|
||||
-122
@@ -1,122 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.configuration
|
||||
|
||||
import com.intellij.ide.util.projectWizard.ModuleWizardStep
|
||||
import com.intellij.ide.util.projectWizard.WizardContext
|
||||
import com.intellij.openapi.externalSystem.service.project.wizard.ExternalModuleSettingsStep
|
||||
import com.intellij.openapi.fileEditor.FileDocumentManager
|
||||
import com.intellij.openapi.module.Module
|
||||
import com.intellij.openapi.projectRoots.Sdk
|
||||
import com.intellij.openapi.roots.ui.configuration.ModulesProvider
|
||||
import com.intellij.openapi.vfs.VfsUtil
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import org.jetbrains.kotlin.idea.KotlinIcons
|
||||
import org.jetbrains.kotlin.idea.util.application.executeCommand
|
||||
import org.jetbrains.kotlin.idea.util.rootManager
|
||||
import org.jetbrains.plugins.gradle.frameworkSupport.BuildScriptDataBuilder
|
||||
import org.jetbrains.plugins.gradle.service.project.wizard.GradleModuleBuilder
|
||||
import org.jetbrains.plugins.gradle.service.settings.GradleProjectSettingsControl
|
||||
import javax.swing.Icon
|
||||
|
||||
class KotlinGradleMultiplatformModuleBuilder : GradleModuleBuilder() {
|
||||
var commonModuleIsParent = false
|
||||
var commonModuleName: String? = null
|
||||
var jvmModuleName: String? = null
|
||||
var jdk: Sdk? = null
|
||||
var jsModuleName: String? = null
|
||||
|
||||
override fun getBuilderId() = "kotlin.gradle.multiplatform"
|
||||
|
||||
override fun getNodeIcon(): Icon = KotlinIcons.MPP
|
||||
|
||||
override fun getPresentableName() = "Kotlin (Multiplatform - Experimental)"
|
||||
|
||||
override fun getDescription() =
|
||||
"Multiplatform projects allow reusing the same code between multiple platforms supported by Kotlin. Such projects are built with Gradle."
|
||||
|
||||
override fun createWizardSteps(wizardContext: WizardContext, modulesProvider: ModulesProvider): Array<ModuleWizardStep> {
|
||||
super.createWizardSteps(wizardContext, modulesProvider) // initializes GradleModuleBuilder.myWizardContext
|
||||
return arrayOf(
|
||||
KotlinGradleMultiplatformWizardStep(this, wizardContext),
|
||||
ExternalModuleSettingsStep(
|
||||
wizardContext, this, GradleProjectSettingsControl(externalProjectSettings)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override fun setupModule(module: Module) {
|
||||
super.setupModule(module)
|
||||
|
||||
val rootDir = module.rootManager.contentRoots.firstOrNull() ?: return
|
||||
val commonDir = setupCommonModule(module, rootDir)
|
||||
val platformRootDir = if (commonModuleIsParent) commonDir else rootDir
|
||||
setupPlatformModule(module, platformRootDir, jvmModuleName, GradleKotlinMPPJavaFrameworkSupportProvider(), jdk)
|
||||
setupPlatformModule(module, platformRootDir, jsModuleName, GradleKotlinMPPJSFrameworkSupportProvider())
|
||||
|
||||
val settingsGradle = rootDir.findChild("settings.gradle")
|
||||
settingsGradle?.let {
|
||||
module.project.executeCommand("Update settings.gradle") {
|
||||
val doc = FileDocumentManager.getInstance().getDocument(it) ?: return@executeCommand
|
||||
val includedModules = listOfNotNull(commonModuleName, jvmModuleName, jsModuleName).filter { it.isNotEmpty() }.map {
|
||||
if (!commonModuleIsParent || it == commonModuleName) it
|
||||
else "$commonModuleName:$it"
|
||||
}
|
||||
if (includedModules.isNotEmpty()) {
|
||||
doc.insertString(doc.textLength, includedModules.joinToString(prefix = "include ") { "'$it'" })
|
||||
}
|
||||
FileDocumentManager.getInstance().saveDocument(doc)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupChildModule(
|
||||
rootModule: Module,
|
||||
parentDir: VirtualFile?,
|
||||
childModuleName: String?,
|
||||
sdk: Sdk? = null,
|
||||
extendScript: (BuildScriptDataBuilder, Sdk?) -> Unit = { _, _ -> }
|
||||
): VirtualFile? {
|
||||
if (parentDir == null || childModuleName.isNullOrEmpty()) return null
|
||||
|
||||
val moduleDir = parentDir.createChildDirectory(this, childModuleName!!)
|
||||
val buildGradle = moduleDir.createChildData(null, "build.gradle")
|
||||
val buildScriptData = BuildScriptDataBuilder(buildGradle)
|
||||
extendScript(buildScriptData, sdk ?: rootModule.rootManager.sdk)
|
||||
VfsUtil.saveText(buildGradle, buildScriptData.build())
|
||||
return moduleDir
|
||||
}
|
||||
|
||||
private fun setupCommonModule(
|
||||
rootModule: Module,
|
||||
parentDir: VirtualFile?
|
||||
) = setupChildModule(rootModule, parentDir, commonModuleName) { builder, sdk ->
|
||||
GradleKotlinMPPCommonFrameworkSupportProvider().addSupport(builder, rootModule, sdk, true)
|
||||
}
|
||||
|
||||
private fun setupPlatformModule(
|
||||
rootModule: Module,
|
||||
parentDir: VirtualFile?,
|
||||
platformModuleName: String?,
|
||||
supportProvider: GradleKotlinFrameworkSupportProvider,
|
||||
sdk: Sdk? = null
|
||||
) = setupChildModule(rootModule, parentDir, platformModuleName, sdk) { builder, finalSdk ->
|
||||
supportProvider.addSupport(builder, rootModule, finalSdk, true)
|
||||
val dependency = commonModuleName ?: ""
|
||||
builder.addDependencyNotation("expectedBy project(\":$dependency\")")
|
||||
}
|
||||
}
|
||||
-86
@@ -1,86 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2018 JetBrains s.r.o. 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.framework.addSupport.FrameworkSupportInModuleConfigurable
|
||||
import com.intellij.ide.util.frameworkSupport.FrameworkSupportModel
|
||||
import com.intellij.openapi.module.Module
|
||||
import com.intellij.openapi.roots.ModifiableModelsProvider
|
||||
import com.intellij.openapi.roots.ModifiableRootModel
|
||||
import com.intellij.openapi.util.Key
|
||||
import com.intellij.openapi.vfs.LocalFileSystem
|
||||
import com.intellij.openapi.vfs.VfsUtil
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import com.intellij.psi.PsiFile
|
||||
import com.intellij.psi.codeStyle.CodeStyleManager
|
||||
import org.jetbrains.kotlin.idea.core.copied
|
||||
import org.jetbrains.kotlin.idea.refactoring.toPsiFile
|
||||
import org.jetbrains.kotlin.psi.UserDataProperty
|
||||
import org.jetbrains.plugins.gradle.frameworkSupport.GradleFrameworkSupportProvider
|
||||
import org.jetbrains.plugins.gradle.service.project.wizard.GradleModuleBuilder
|
||||
import org.jetbrains.plugins.gradle.util.GradleConstants
|
||||
import java.io.File
|
||||
|
||||
internal var Module.gradleModuleBuilder: GradleModuleBuilder? by UserDataProperty(Key.create("GRADLE_MODULE_BUILDER"))
|
||||
private var Module.editableSettingsPsiFileCopy: PsiFile? by UserDataProperty(Key.create("EDITABLE_SETTINGS_PSI_FILE_COPY"))
|
||||
|
||||
internal fun findSettingsGradleFile(module: Module): VirtualFile? {
|
||||
val contentEntryPath = module.gradleModuleBuilder?.contentEntryPath ?: return null
|
||||
if (contentEntryPath.isEmpty()) return null
|
||||
val contentRootDir = File(contentEntryPath)
|
||||
val modelContentRootDir = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(contentRootDir) ?: return null
|
||||
return modelContentRootDir.findChild(GradleConstants.SETTINGS_FILE_NAME)
|
||||
?: module.project.baseDir.findChild(GradleConstants.SETTINGS_FILE_NAME)
|
||||
}
|
||||
|
||||
// Circumvent write actions and modify the file directly
|
||||
// TODO: Get rid of this hack when IDEA API allows manipulation of settings script similarly to the main script itself
|
||||
internal fun updateSettingsScript(module: Module, updater: (PsiFile) -> Unit) {
|
||||
val storedCopy = module.editableSettingsPsiFileCopy
|
||||
val settingsPsiCopy = storedCopy ?: findSettingsGradleFile(module)?.toPsiFile(module.project)?.copied() ?: return
|
||||
if (storedCopy == null) {
|
||||
module.editableSettingsPsiFileCopy = settingsPsiCopy
|
||||
}
|
||||
updater(settingsPsiCopy)
|
||||
}
|
||||
|
||||
internal fun flushSettingsGradleCopy(module: Module) {
|
||||
try {
|
||||
val settingsFile = findSettingsGradleFile(module)
|
||||
val settingsPsiCopy = module.editableSettingsPsiFileCopy
|
||||
if (settingsPsiCopy != null && settingsFile != null) {
|
||||
CodeStyleManager.getInstance(module.project).reformat(settingsPsiCopy)
|
||||
VfsUtil.saveText(settingsFile, settingsPsiCopy.text)
|
||||
}
|
||||
} finally {
|
||||
module.gradleModuleBuilder = null
|
||||
module.editableSettingsPsiFileCopy = null
|
||||
}
|
||||
}
|
||||
|
||||
class KotlinGradleFrameworkSupportInModuleConfigurable(
|
||||
private val model: FrameworkSupportModel,
|
||||
private val supportProvider: GradleFrameworkSupportProvider
|
||||
) : FrameworkSupportInModuleConfigurable() {
|
||||
override fun createComponent() = null
|
||||
|
||||
override fun addSupport(
|
||||
module: Module,
|
||||
rootModel: ModifiableRootModel,
|
||||
modifiableModelsProvider: ModifiableModelsProvider
|
||||
) {
|
||||
val buildScriptData = GradleModuleBuilder.getBuildScriptData(module)
|
||||
if (buildScriptData != null) {
|
||||
val builder = model.moduleBuilder
|
||||
try {
|
||||
module.gradleModuleBuilder = builder as? GradleModuleBuilder
|
||||
supportProvider.addSupport(module, rootModel, modifiableModelsProvider, buildScriptData)
|
||||
} finally {
|
||||
flushSettingsGradleCopy(module)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
-408
@@ -1,408 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2016 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.codeInsight.gradle
|
||||
|
||||
import com.intellij.codeInspection.LocalInspectionTool
|
||||
import com.intellij.codeInspection.ProblemDescriptorBase
|
||||
import com.intellij.openapi.util.Ref
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import org.jetbrains.kotlin.idea.inspections.gradle.DeprecatedGradleDependencyInspection
|
||||
import org.jetbrains.kotlin.idea.inspections.gradle.DifferentKotlinGradleVersionInspection
|
||||
import org.jetbrains.kotlin.idea.inspections.gradle.DifferentStdlibGradleVersionInspection
|
||||
import org.jetbrains.kotlin.idea.inspections.gradle.GradleKotlinxCoroutinesDeprecationInspection
|
||||
import org.jetbrains.kotlin.idea.inspections.runInspection
|
||||
import org.junit.Assert
|
||||
import org.junit.Test
|
||||
|
||||
class GradleInspectionTest : GradleImportingTestCase() {
|
||||
@Test
|
||||
fun testDifferentStdlibGradleVersion() {
|
||||
val localFile = createProjectSubFile(
|
||||
"build.gradle", """
|
||||
group 'Again'
|
||||
version '1.0-SNAPSHOT'
|
||||
|
||||
buildscript {
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.0.2")
|
||||
}
|
||||
}
|
||||
|
||||
apply plugin: 'kotlin'
|
||||
|
||||
dependencies {
|
||||
compile "org.jetbrains.kotlin:kotlin-stdlib:1.0.3"
|
||||
}
|
||||
"""
|
||||
)
|
||||
importProject()
|
||||
|
||||
val tool = DifferentStdlibGradleVersionInspection()
|
||||
val problems = getInspectionResult(tool, localFile)
|
||||
|
||||
Assert.assertTrue(problems.size == 1)
|
||||
Assert.assertEquals("Plugin version (1.0.2) is not the same as library version (1.0.3)", problems.single())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testDifferentStdlibGradleVersionWithImplementation() {
|
||||
val localFile = createProjectSubFile(
|
||||
"build.gradle", """
|
||||
group 'Again'
|
||||
version '1.0-SNAPSHOT'
|
||||
|
||||
buildscript {
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.0.2")
|
||||
}
|
||||
}
|
||||
|
||||
apply plugin: 'kotlin'
|
||||
|
||||
dependencies {
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib:1.0.3"
|
||||
}
|
||||
"""
|
||||
)
|
||||
importProject()
|
||||
|
||||
val tool = DifferentStdlibGradleVersionInspection()
|
||||
val problems = getInspectionResult(tool, localFile)
|
||||
|
||||
Assert.assertTrue(problems.size == 1)
|
||||
Assert.assertEquals("Plugin version (1.0.2) is not the same as library version (1.0.3)", problems.single())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testDifferentStdlibJre7GradleVersion() {
|
||||
val localFile = createProjectSubFile(
|
||||
"build.gradle", """
|
||||
group 'Again'
|
||||
version '1.0-SNAPSHOT'
|
||||
|
||||
buildscript {
|
||||
repositories {
|
||||
mavenCentral()
|
||||
maven {
|
||||
url 'http://dl.bintray.com/kotlin/kotlin-eap-1.1'
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.1.0-beta-17")
|
||||
}
|
||||
}
|
||||
|
||||
apply plugin: 'kotlin'
|
||||
|
||||
dependencies {
|
||||
compile "org.jetbrains.kotlin:kotlin-stdlib-jre7:1.1.0-beta-22"
|
||||
}
|
||||
"""
|
||||
)
|
||||
importProject()
|
||||
|
||||
val tool = DifferentStdlibGradleVersionInspection()
|
||||
val problems = getInspectionResult(tool, localFile)
|
||||
|
||||
Assert.assertTrue(problems.size == 1)
|
||||
Assert.assertEquals("Plugin version (1.1.0-beta-17) is not the same as library version (1.1.0-beta-22)", problems.single())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testDifferentStdlibJdk7GradleVersion() {
|
||||
val localFile = createProjectSubFile(
|
||||
"build.gradle", """
|
||||
group 'Again'
|
||||
version '1.0-SNAPSHOT'
|
||||
|
||||
buildscript {
|
||||
repositories {
|
||||
mavenCentral()
|
||||
maven {
|
||||
url 'http://dl.bintray.com/kotlin/kotlin-eap-1.1'
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.1.0-beta-17")
|
||||
}
|
||||
}
|
||||
|
||||
apply plugin: 'kotlin'
|
||||
|
||||
dependencies {
|
||||
compile "org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.1.0-beta-22"
|
||||
}
|
||||
"""
|
||||
)
|
||||
importProject()
|
||||
|
||||
val tool = DifferentStdlibGradleVersionInspection()
|
||||
val problems = getInspectionResult(tool, localFile)
|
||||
|
||||
Assert.assertTrue(problems.size == 1)
|
||||
Assert.assertEquals("Plugin version (1.1.0-beta-17) is not the same as library version (1.1.0-beta-22)", problems.single())
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
fun testDifferentStdlibGradleVersionWithVariables() {
|
||||
createProjectSubFile(
|
||||
"gradle.properties", """
|
||||
|kotlin=1.0.1
|
||||
|lib_version=1.0.3""".trimMargin()
|
||||
)
|
||||
val localFile = createProjectSubFile(
|
||||
"build.gradle", """
|
||||
group 'Again'
|
||||
version '1.0-SNAPSHOT'
|
||||
|
||||
buildscript {
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:${'$'}kotlin")
|
||||
}
|
||||
}
|
||||
|
||||
apply plugin: 'kotlin'
|
||||
|
||||
dependencies {
|
||||
compile group: 'org.jetbrains.kotlin', name: 'kotlin-stdlib', version: lib_version
|
||||
}
|
||||
"""
|
||||
)
|
||||
importProject()
|
||||
|
||||
val tool = DifferentStdlibGradleVersionInspection()
|
||||
val problems = getInspectionResult(tool, localFile)
|
||||
|
||||
Assert.assertTrue(problems.size == 1)
|
||||
Assert.assertEquals("Plugin version (1.0.1) is not the same as library version (1.0.3)", problems.single())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testDifferentKotlinGradleVersion() {
|
||||
createProjectSubFile("gradle.properties", """test=1.0.1""")
|
||||
val localFile = createProjectSubFile(
|
||||
"build.gradle", """
|
||||
group 'Again'
|
||||
version '1.0-SNAPSHOT'
|
||||
|
||||
buildscript {
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:${'$'}{test}")
|
||||
}
|
||||
}
|
||||
|
||||
apply plugin: 'kotlin'
|
||||
"""
|
||||
)
|
||||
importProject()
|
||||
|
||||
val tool = DifferentKotlinGradleVersionInspection()
|
||||
tool.testVersionMessage = "\$PLUGIN_VERSION"
|
||||
val problems = getInspectionResult(tool, localFile)
|
||||
|
||||
Assert.assertTrue(problems.size == 1)
|
||||
Assert.assertEquals(
|
||||
"Kotlin version that is used for building with Gradle (1.0.1) differs from the one bundled into the IDE plugin (\$PLUGIN_VERSION)",
|
||||
problems.single()
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testJreInOldVersion() {
|
||||
val localFile = createProjectSubFile(
|
||||
"build.gradle", """
|
||||
group 'Again'
|
||||
version '1.0-SNAPSHOT'
|
||||
|
||||
buildscript {
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.1.60")
|
||||
}
|
||||
}
|
||||
|
||||
apply plugin: 'kotlin'
|
||||
|
||||
dependencies {
|
||||
compile "org.jetbrains.kotlin:kotlin-stdlib-jre8:1.1.60"
|
||||
}
|
||||
"""
|
||||
)
|
||||
importProject()
|
||||
|
||||
val tool = DeprecatedGradleDependencyInspection()
|
||||
val problems = getInspectionResult(tool, localFile)
|
||||
|
||||
Assert.assertTrue(problems.isEmpty())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testJreIsDeprecated() {
|
||||
val localFile = createProjectSubFile(
|
||||
"build.gradle", """
|
||||
group 'Again'
|
||||
version '1.0-SNAPSHOT'
|
||||
|
||||
buildscript {
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.1.60")
|
||||
}
|
||||
}
|
||||
|
||||
apply plugin: 'kotlin'
|
||||
|
||||
dependencies {
|
||||
compile "org.jetbrains.kotlin:kotlin-stdlib-jre7:1.2.0"
|
||||
}
|
||||
"""
|
||||
)
|
||||
importProject()
|
||||
|
||||
val tool = DeprecatedGradleDependencyInspection()
|
||||
val problems = getInspectionResult(tool, localFile)
|
||||
|
||||
Assert.assertTrue(problems.size == 1)
|
||||
Assert.assertEquals(
|
||||
"kotlin-stdlib-jre7 is deprecated since 1.2.0 and should be replaced with kotlin-stdlib-jdk7",
|
||||
problems.single()
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testJreIsDeprecatedWithImplementation() {
|
||||
val localFile = createProjectSubFile(
|
||||
"build.gradle", """
|
||||
group 'Again'
|
||||
version '1.0-SNAPSHOT'
|
||||
|
||||
buildscript {
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.2.0")
|
||||
}
|
||||
}
|
||||
|
||||
apply plugin: 'kotlin'
|
||||
|
||||
dependencies {
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jre7:1.2.0"
|
||||
}
|
||||
"""
|
||||
)
|
||||
importProject()
|
||||
|
||||
val tool = DeprecatedGradleDependencyInspection()
|
||||
val problems = getInspectionResult(tool, localFile)
|
||||
|
||||
Assert.assertTrue(problems.size == 1)
|
||||
Assert.assertEquals(
|
||||
"kotlin-stdlib-jre7 is deprecated since 1.2.0 and should be replaced with kotlin-stdlib-jdk7",
|
||||
problems.single()
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testObsoleteCoroutinesUsage() {
|
||||
val localFile = createProjectSubFile(
|
||||
"build.gradle", """
|
||||
group 'Again'
|
||||
version '1.0-SNAPSHOT'
|
||||
|
||||
buildscript {
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.2.0")
|
||||
}
|
||||
}
|
||||
|
||||
apply plugin: 'kotlin'
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
maven { url "https://kotlin.bintray.com/kotlinx" }
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compile 'org.jetbrains.kotlinx:kotlinx-coroutines-core:0.23.4'
|
||||
}
|
||||
|
||||
compileKotlin {
|
||||
kotlinOptions.languageVersion = "1.3"
|
||||
}
|
||||
"""
|
||||
)
|
||||
importProject()
|
||||
|
||||
val tool = GradleKotlinxCoroutinesDeprecationInspection()
|
||||
val problems = getInspectionResult(tool, localFile)
|
||||
|
||||
Assert.assertTrue(problems.size == 1)
|
||||
Assert.assertEquals(
|
||||
"Library should be updated to be compatible with Kotlin 1.3",
|
||||
problems.single()
|
||||
)
|
||||
}
|
||||
|
||||
fun getInspectionResult(tool: LocalInspectionTool, file: VirtualFile): List<String> {
|
||||
val resultRef = Ref<List<String>>()
|
||||
invokeTestRunnable {
|
||||
val presentation = runInspection(tool, myProject, listOf(file))
|
||||
|
||||
val foundProblems = presentation.problemElements
|
||||
.values
|
||||
.flatMap { it.toList() }
|
||||
.mapNotNull { it as? ProblemDescriptorBase }
|
||||
.map { it.descriptionTemplate }
|
||||
|
||||
resultRef.set(foundProblems)
|
||||
}
|
||||
|
||||
return resultRef.get()
|
||||
}
|
||||
}
|
||||
-334
@@ -1,334 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2016 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.debugger
|
||||
|
||||
import com.intellij.debugger.SourcePosition
|
||||
import com.intellij.debugger.engine.DebugProcess
|
||||
import com.intellij.debugger.jdi.VirtualMachineProxyImpl
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.compiler.CompilerPaths
|
||||
import com.intellij.openapi.compiler.ex.CompilerPathsEx
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.roots.ProjectFileIndex
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import com.intellij.psi.search.GlobalSearchScope
|
||||
import com.intellij.util.containers.ConcurrentWeakFactoryMap
|
||||
import com.intellij.util.containers.ContainerUtil
|
||||
import com.sun.jdi.Location
|
||||
import com.sun.jdi.ReferenceType
|
||||
import org.jetbrains.kotlin.codegen.inline.API
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
||||
import org.jetbrains.kotlin.idea.debugger.evaluate.KotlinDebuggerCaches
|
||||
import org.jetbrains.kotlin.idea.refactoring.getLineCount
|
||||
import org.jetbrains.kotlin.idea.refactoring.getLineStartOffset
|
||||
import org.jetbrains.kotlin.idea.refactoring.toPsiFile
|
||||
import org.jetbrains.kotlin.idea.util.ProjectRootsUtil
|
||||
import org.jetbrains.kotlin.idea.util.application.runReadAction
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
import org.jetbrains.kotlin.load.kotlin.VirtualFileFinder
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.name.tail
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.parents
|
||||
import org.jetbrains.kotlin.resolve.inline.InlineUtil
|
||||
import org.jetbrains.kotlin.resolve.jvm.JvmClassName
|
||||
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
|
||||
import org.jetbrains.kotlin.utils.getOrPutNullable
|
||||
import org.jetbrains.org.objectweb.asm.*
|
||||
import java.io.File
|
||||
import java.util.*
|
||||
|
||||
fun isInlineFunctionLineNumber(file: VirtualFile, lineNumber: Int, project: Project): Boolean {
|
||||
if (ProjectRootsUtil.isProjectSourceFile(project, file)) {
|
||||
val linesInFile = file.toPsiFile(project)?.getLineCount() ?: return false
|
||||
return lineNumber > linesInFile
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
fun readBytecodeInfo(project: Project,
|
||||
jvmName: JvmClassName,
|
||||
file: VirtualFile): BytecodeDebugInfo? {
|
||||
return KotlinDebuggerCaches.getOrReadDebugInfoFromBytecode(project, jvmName, file)
|
||||
}
|
||||
|
||||
fun ktLocationInfo(location: Location, isDexDebug: Boolean, project: Project,
|
||||
preferInlined: Boolean = false, locationFile: KtFile? = null): Pair<Int, KtFile?> {
|
||||
if (isDexDebug && (locationFile == null || location.lineNumber() > locationFile.getLineCount())) {
|
||||
if (!preferInlined) {
|
||||
val thisFunLine = runReadAction { getLastLineNumberForLocation(location, project) }
|
||||
if (thisFunLine != null && thisFunLine != location.lineNumber()) {
|
||||
return thisFunLine to locationFile
|
||||
}
|
||||
}
|
||||
|
||||
val inlinePosition = runReadAction { getOriginalPositionOfInlinedLine(location, project) }
|
||||
if (inlinePosition != null) {
|
||||
val (file, line) = inlinePosition
|
||||
return line + 1 to file
|
||||
}
|
||||
}
|
||||
|
||||
return location.lineNumber() to locationFile
|
||||
}
|
||||
|
||||
/**
|
||||
* Only the first line number is stored for instruction in dex. It can be obtained through location.lineNumber().
|
||||
* This method allows to get last stored linenumber for instruction.
|
||||
*/
|
||||
fun getLastLineNumberForLocation(location: Location, project: Project, searchScope: GlobalSearchScope = GlobalSearchScope.allScope(project)): Int? {
|
||||
val lineNumber = location.lineNumber()
|
||||
val fqName = FqName(location.declaringType().name())
|
||||
val fileName = location.sourceName()
|
||||
|
||||
val method = location.method() ?: return null
|
||||
val name = method.name() ?: return null
|
||||
val signature = method.signature() ?: return null
|
||||
|
||||
val debugInfo = findAndReadClassFile(fqName, fileName, project, searchScope, { isInlineFunctionLineNumber(it, lineNumber, project) }) ?: return null
|
||||
|
||||
val lineMapping = debugInfo.lineTableMapping[BytecodeMethodKey(name, signature)] ?: return null
|
||||
return lineMapping.values.firstOrNull { it.contains(lineNumber) }?.last()
|
||||
}
|
||||
|
||||
class WeakBytecodeDebugInfoStorage : ConcurrentWeakFactoryMap<BinaryCacheKey, BytecodeDebugInfo?>() {
|
||||
override fun create(key: BinaryCacheKey): BytecodeDebugInfo? {
|
||||
val bytes = readClassFileImpl(key.project, key.jvmName, key.file) ?: return null
|
||||
|
||||
val smapData = readDebugInfo(bytes)
|
||||
val lineNumberMapping = readLineNumberTableMapping(bytes)
|
||||
|
||||
return BytecodeDebugInfo(smapData, lineNumberMapping)
|
||||
}
|
||||
override fun createMap(): Map<BinaryCacheKey, BytecodeDebugInfo?> {
|
||||
return ContainerUtil.createConcurrentWeakKeyWeakValueMap()
|
||||
}
|
||||
}
|
||||
|
||||
class BytecodeDebugInfo(val smapData: SmapData?, val lineTableMapping: Map<BytecodeMethodKey, Map<String, Set<Int>>>)
|
||||
|
||||
data class BytecodeMethodKey(val methodName: String, val signature: String)
|
||||
|
||||
data class BinaryCacheKey(val project: Project, val jvmName: JvmClassName, val file: VirtualFile)
|
||||
|
||||
private fun readClassFileImpl(project: Project,
|
||||
jvmName: JvmClassName,
|
||||
file: VirtualFile): ByteArray? {
|
||||
val fqNameWithInners = jvmName.fqNameForClassNameWithoutDollars.tail(jvmName.packageFqName)
|
||||
|
||||
fun readFromLibrary(): ByteArray? {
|
||||
if (!ProjectRootsUtil.isLibrarySourceFile(project, file)) return null
|
||||
|
||||
val classId = ClassId(jvmName.packageFqName, Name.identifier(fqNameWithInners.asString()))
|
||||
|
||||
val fileFinder = VirtualFileFinder.getInstance(project)
|
||||
val classFile = fileFinder.findVirtualFileWithHeader(classId) ?: return null
|
||||
return classFile.contentsToByteArray(false)
|
||||
}
|
||||
|
||||
fun readFromOutput(isForTestClasses: Boolean): ByteArray? {
|
||||
if (!ProjectRootsUtil.isProjectSourceFile(project, file)) return null
|
||||
|
||||
val module = ProjectFileIndex.SERVICE.getInstance(project).getModuleForFile(file) ?: return null
|
||||
|
||||
val outputPaths = CompilerPathsEx.getOutputPaths(arrayOf(module)).toList()
|
||||
val className = fqNameWithInners.asString().replace('.', '$')
|
||||
var classFile = findClassFileByPaths(jvmName.packageFqName.asString(), className, outputPaths)
|
||||
|
||||
if (classFile == null) {
|
||||
if (!isForTestClasses) {
|
||||
return null
|
||||
}
|
||||
|
||||
val outputDir = CompilerPaths.getModuleOutputDirectory(module, /*forTests = */ isForTestClasses) ?: return null
|
||||
|
||||
val outputModeDirName = outputDir.name
|
||||
// FIXME: It looks like this doesn't work anymore after Kotlin gradle plugin have stopped generating Kotlin classes in java output dir
|
||||
// Originally this code did mapping like 'path/classes/test/debug' -> 'path/classes/androidTest/debug'
|
||||
val androidTestOutputDir = outputDir.parent?.parent?.findChild("androidTest")?.findChild(outputModeDirName) ?: return null
|
||||
|
||||
classFile = findClassFileByPath(jvmName.packageFqName.asString(), className, androidTestOutputDir.path) ?: return null
|
||||
}
|
||||
|
||||
return classFile.readBytes()
|
||||
}
|
||||
|
||||
fun readFromSourceOutput(): ByteArray? = readFromOutput(false)
|
||||
|
||||
fun readFromTestOutput(): ByteArray? = readFromOutput(true)
|
||||
|
||||
return readFromLibrary() ?:
|
||||
readFromSourceOutput() ?:
|
||||
readFromTestOutput()
|
||||
}
|
||||
|
||||
private fun findClassFileByPaths(packageName: String, className: String, paths: List<String>): File? =
|
||||
paths.mapNotNull { path -> findClassFileByPath(packageName, className, path) }.maxBy { it.lastModified() }
|
||||
|
||||
private fun findClassFileByPath(packageName: String, className: String, outputDirPath: String): File? {
|
||||
val outDirFile = File(outputDirPath).takeIf(File::exists) ?: return null
|
||||
|
||||
val parentDirectory = File(outDirFile, packageName.replace(".", File.separator))
|
||||
if (!parentDirectory.exists()) return null
|
||||
|
||||
if (ApplicationManager.getApplication().isUnitTestMode) {
|
||||
val beforeDexFileClassFile = File(parentDirectory, className + ".class.before_dex")
|
||||
if (beforeDexFileClassFile.exists()) {
|
||||
return beforeDexFileClassFile
|
||||
}
|
||||
}
|
||||
|
||||
val classFile = File(parentDirectory, className + ".class")
|
||||
if (classFile.exists()) {
|
||||
return classFile
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
private fun readLineNumberTableMapping(bytes: ByteArray): Map<BytecodeMethodKey, Map<String, Set<Int>>> {
|
||||
val lineNumberMapping = HashMap<BytecodeMethodKey, Map<String, Set<Int>>>()
|
||||
|
||||
ClassReader(bytes).accept(object : ClassVisitor(API) {
|
||||
override fun visitMethod(access: Int, name: String?, desc: String?, signature: String?, exceptions: Array<out String>?): MethodVisitor? {
|
||||
if (name == null || desc == null) {
|
||||
return null
|
||||
}
|
||||
|
||||
val methodKey = BytecodeMethodKey(name, desc)
|
||||
val methodLinesMapping = HashMap<String, MutableSet<Int>>()
|
||||
lineNumberMapping[methodKey] = methodLinesMapping
|
||||
|
||||
return object : MethodVisitor(Opcodes.ASM5, null) {
|
||||
override fun visitLineNumber(line: Int, start: Label?) {
|
||||
if (start != null) {
|
||||
methodLinesMapping.getOrPutNullable(start.toString(), { LinkedHashSet<Int>() }).add(line)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}, ClassReader.SKIP_FRAMES and ClassReader.SKIP_CODE)
|
||||
|
||||
return lineNumberMapping
|
||||
}
|
||||
|
||||
internal fun getOriginalPositionOfInlinedLine(location: Location, project: Project): Pair<KtFile, Int>? {
|
||||
val lineNumber = location.lineNumber()
|
||||
val fqName = FqName(location.declaringType().name())
|
||||
val fileName = location.sourceName()
|
||||
val searchScope = GlobalSearchScope.allScope(project)
|
||||
|
||||
val debugInfo = findAndReadClassFile(fqName, fileName, project, searchScope, { isInlineFunctionLineNumber(it, lineNumber, project) }) ?:
|
||||
return null
|
||||
val smapData = debugInfo.smapData ?: return null
|
||||
|
||||
return mapStacktraceLineToSource(smapData, lineNumber, project, SourceLineKind.EXECUTED_LINE, searchScope)
|
||||
}
|
||||
|
||||
private fun findAndReadClassFile(
|
||||
fqName: FqName, fileName: String, project: Project, searchScope: GlobalSearchScope,
|
||||
fileFilter: (VirtualFile) -> Boolean): BytecodeDebugInfo? {
|
||||
val internalName = fqName.asString().replace('.', '/')
|
||||
val jvmClassName = JvmClassName.byInternalName(internalName)
|
||||
|
||||
val file = DebuggerUtils.findSourceFileForClassIncludeLibrarySources(project, searchScope, jvmClassName, fileName) ?: return null
|
||||
|
||||
val virtualFile = file.virtualFile ?: return null
|
||||
if (!fileFilter(virtualFile)) return null
|
||||
|
||||
return readBytecodeInfo(project, jvmClassName, virtualFile)
|
||||
}
|
||||
|
||||
internal fun getLocationsOfInlinedLine(type: ReferenceType, position: SourcePosition, sourceSearchScope: GlobalSearchScope): List<Location> {
|
||||
val line = position.line
|
||||
val file = position.file
|
||||
val project = position.file.project
|
||||
|
||||
val lineStartOffset = file.getLineStartOffset(line) ?: return listOf()
|
||||
val element = file.findElementAt(lineStartOffset) ?: return listOf()
|
||||
val ktElement = element.parents.firstIsInstanceOrNull<KtElement>() ?: return listOf()
|
||||
|
||||
val isInInline = runReadAction { element.parents.any { it is KtFunction && it.hasModifier(KtTokens.INLINE_KEYWORD) } }
|
||||
|
||||
if (!isInInline) {
|
||||
// Lambdas passed to crossinline arguments are inlined when they are used in non-inlined lambdas
|
||||
val isInCrossinlineArgument = isInCrossinlineArgument(ktElement)
|
||||
if (!isInCrossinlineArgument) {
|
||||
return listOf()
|
||||
}
|
||||
}
|
||||
|
||||
val lines = inlinedLinesNumbers(line + 1, position.file.name, FqName(type.name()), type.sourceName(), project, sourceSearchScope)
|
||||
|
||||
return lines.flatMap { type.locationsOfLine(it) }
|
||||
}
|
||||
|
||||
fun isInCrossinlineArgument(ktElement: KtElement): Boolean {
|
||||
val argumentFunctions = runReadAction {
|
||||
ktElement.parents.filter {
|
||||
when (it) {
|
||||
is KtFunctionLiteral -> it.parent is KtLambdaExpression && (it.parent.parent is KtValueArgument || it.parent.parent is KtLambdaArgument)
|
||||
is KtFunction -> it.parent is KtValueArgument
|
||||
else -> false
|
||||
}
|
||||
}.filterIsInstance<KtFunction>()
|
||||
}
|
||||
|
||||
val bindingContext = ktElement.analyze(BodyResolveMode.PARTIAL)
|
||||
return argumentFunctions.any {
|
||||
val argumentDescriptor = InlineUtil.getInlineArgumentDescriptor(it, bindingContext)
|
||||
argumentDescriptor?.isCrossinline ?: false
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private fun inlinedLinesNumbers(
|
||||
inlineLineNumber: Int, inlineFileName: String,
|
||||
destinationTypeFqName: FqName, destinationFileName: String,
|
||||
project: Project, sourceSearchScope: GlobalSearchScope): List<Int> {
|
||||
val internalName = destinationTypeFqName.asString().replace('.', '/')
|
||||
val jvmClassName = JvmClassName.byInternalName(internalName)
|
||||
|
||||
val file = DebuggerUtils.findSourceFileForClassIncludeLibrarySources(project, sourceSearchScope, jvmClassName, destinationFileName) ?:
|
||||
return listOf()
|
||||
|
||||
val virtualFile = file.virtualFile ?: return listOf()
|
||||
|
||||
val debugInfo = readBytecodeInfo(project, jvmClassName, virtualFile) ?: return listOf()
|
||||
val smapData = debugInfo.smapData ?: return listOf()
|
||||
|
||||
val smap = smapData.kotlinStrata ?: return listOf()
|
||||
|
||||
val mappingsToInlinedFile = smap.fileMappings.filter { it.name == inlineFileName }
|
||||
val mappingIntervals = mappingsToInlinedFile.flatMap { it.lineMappings }
|
||||
|
||||
return mappingIntervals.asSequence().
|
||||
filter { rangeMapping -> rangeMapping.hasMappingForSource(inlineLineNumber) }.
|
||||
map { rangeMapping -> rangeMapping.mapSourceToDest(inlineLineNumber) }.
|
||||
filter { line -> line != -1 }.
|
||||
toList()
|
||||
}
|
||||
|
||||
@Volatile var emulateDexDebugInTests: Boolean = false
|
||||
|
||||
fun DebugProcess.isDexDebug() =
|
||||
(emulateDexDebugInTests && ApplicationManager.getApplication().isUnitTestMode) ||
|
||||
(this.virtualMachineProxy as? VirtualMachineProxyImpl)?.virtualMachine?.name() == "Dalvik" // TODO: check other machine names
|
||||
@@ -1,380 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2016 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.injection
|
||||
|
||||
import com.intellij.codeInsight.AnnotationUtil
|
||||
import com.intellij.lang.injection.MultiHostInjector
|
||||
import com.intellij.lang.injection.MultiHostRegistrar
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.progress.ProgressManager
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.util.Key
|
||||
import com.intellij.psi.*
|
||||
import com.intellij.psi.impl.source.tree.injected.InjectedLanguageUtil
|
||||
import com.intellij.psi.search.LocalSearchScope
|
||||
import com.intellij.psi.search.searches.ReferencesSearch
|
||||
import com.intellij.psi.util.PsiTreeUtil
|
||||
import org.intellij.plugins.intelliLang.Configuration
|
||||
import org.intellij.plugins.intelliLang.inject.InjectorUtils
|
||||
import org.intellij.plugins.intelliLang.inject.LanguageInjectionSupport
|
||||
import org.intellij.plugins.intelliLang.inject.TemporaryPlacesRegistry
|
||||
import org.intellij.plugins.intelliLang.inject.config.BaseInjection
|
||||
import org.intellij.plugins.intelliLang.inject.java.JavaLanguageInjectionSupport
|
||||
import org.intellij.plugins.intelliLang.util.AnnotationUtilEx
|
||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.annotations.Annotated
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
||||
import org.jetbrains.kotlin.idea.references.KtReference
|
||||
import org.jetbrains.kotlin.idea.references.mainReference
|
||||
import org.jetbrains.kotlin.idea.runInReadActionWithWriteActionPriority
|
||||
import org.jetbrains.kotlin.idea.search.usagesSearch.descriptor
|
||||
import org.jetbrains.kotlin.idea.util.ProjectRootsUtil
|
||||
import org.jetbrains.kotlin.idea.util.application.progressIndicatorNullable
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.resolve.annotations.argumentValue
|
||||
import org.jetbrains.kotlin.resolve.constants.StringValue
|
||||
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
||||
import java.util.*
|
||||
import kotlin.collections.ArrayList
|
||||
|
||||
class KotlinLanguageInjector(
|
||||
val configuration: Configuration,
|
||||
val project: Project,
|
||||
val temporaryPlacesRegistry: TemporaryPlacesRegistry
|
||||
) : MultiHostInjector {
|
||||
companion object {
|
||||
private val STRING_LITERALS_REGEXP = "\"([^\"]*)\"".toRegex()
|
||||
private val ABSENT_KOTLIN_INJECTION = BaseInjection("ABSENT_KOTLIN_BASE_INJECTION")
|
||||
}
|
||||
|
||||
val kotlinSupport: KotlinLanguageInjectionSupport? by lazy {
|
||||
ArrayList(InjectorUtils.getActiveInjectionSupports()).filterIsInstance(KotlinLanguageInjectionSupport::class.java).firstOrNull()
|
||||
}
|
||||
|
||||
private data class KotlinCachedInjection(val modificationCount: Long, val baseInjection: BaseInjection)
|
||||
private var KtStringTemplateExpression.cachedInjectionWithModification: KotlinCachedInjection? by UserDataProperty(
|
||||
Key.create<KotlinCachedInjection>("CACHED_INJECTION_WITH_MODIFICATION"))
|
||||
|
||||
override fun getLanguagesToInject(registrar: MultiHostRegistrar, context: PsiElement) {
|
||||
val ktHost: KtStringTemplateExpression = context as? KtStringTemplateExpression ?: return
|
||||
if (!context.isValidHost) return
|
||||
|
||||
val support = kotlinSupport ?: return
|
||||
|
||||
if (!ProjectRootsUtil.isInProjectOrLibSource(ktHost)) return
|
||||
|
||||
val needImmediateAnswer = with(ApplicationManager.getApplication()) { isDispatchThread && !isUnitTestMode }
|
||||
val kotlinCachedInjection = ktHost.cachedInjectionWithModification
|
||||
val modificationCount = PsiManager.getInstance(project).modificationTracker.modificationCount
|
||||
|
||||
val baseInjection = when {
|
||||
needImmediateAnswer -> {
|
||||
// Can't afford long counting or typing will be laggy. Force cache reuse even if it's outdated.
|
||||
kotlinCachedInjection?.baseInjection ?: ABSENT_KOTLIN_INJECTION
|
||||
}
|
||||
kotlinCachedInjection != null && (modificationCount == kotlinCachedInjection.modificationCount) ->
|
||||
// Cache is up-to-date
|
||||
kotlinCachedInjection.baseInjection
|
||||
else -> {
|
||||
fun computeAndCache(): BaseInjection {
|
||||
val computedInjection = computeBaseInjection(ktHost, support, registrar) ?: ABSENT_KOTLIN_INJECTION
|
||||
ktHost.cachedInjectionWithModification = KotlinCachedInjection(modificationCount, computedInjection)
|
||||
return computedInjection
|
||||
}
|
||||
|
||||
if (ApplicationManager.getApplication().isReadAccessAllowed && ProgressManager.getInstance().progressIndicatorNullable == null) {
|
||||
// The action cannot be canceled by caller and by internal checkCanceled() calls.
|
||||
// Force creating new indicator that is canceled on write action start, otherwise there might be lags in typing.
|
||||
runInReadActionWithWriteActionPriority(::computeAndCache) ?: kotlinCachedInjection?.baseInjection ?: ABSENT_KOTLIN_INJECTION
|
||||
}
|
||||
else {
|
||||
computeAndCache()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (baseInjection == ABSENT_KOTLIN_INJECTION) {
|
||||
return
|
||||
}
|
||||
|
||||
val language = InjectorUtils.getLanguageByString(baseInjection.injectedLanguageId) ?: return
|
||||
|
||||
if (ktHost.hasInterpolation()) {
|
||||
val file = ktHost.containingKtFile
|
||||
val parts = splitLiteralToInjectionParts(baseInjection, ktHost) ?: return
|
||||
|
||||
if (parts.ranges.isEmpty()) return
|
||||
|
||||
InjectorUtils.registerInjection(language, parts.ranges, file, registrar)
|
||||
InjectorUtils.registerSupport(support, false, registrar)
|
||||
InjectorUtils.putInjectedFileUserData(registrar, InjectedLanguageUtil.FRANKENSTEIN_INJECTION,
|
||||
if (parts.isUnparsable) java.lang.Boolean.TRUE else null)
|
||||
}
|
||||
else {
|
||||
InjectorUtils.registerInjectionSimple(ktHost, baseInjection, support, registrar)
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("FoldInitializerAndIfToElvis")
|
||||
private fun computeBaseInjection(
|
||||
ktHost: KtStringTemplateExpression,
|
||||
support: KotlinLanguageInjectionSupport,
|
||||
registrar: MultiHostRegistrar): BaseInjection? {
|
||||
val containingFile = ktHost.containingFile
|
||||
|
||||
val tempInjectedLanguage = temporaryPlacesRegistry.getLanguageFor(ktHost, containingFile)
|
||||
if (tempInjectedLanguage != null) {
|
||||
InjectorUtils.putInjectedFileUserData(registrar, LanguageInjectionSupport.TEMPORARY_INJECTED_LANGUAGE, tempInjectedLanguage)
|
||||
return BaseInjection(support.id).apply {
|
||||
injectedLanguageId = tempInjectedLanguage.id
|
||||
prefix = tempInjectedLanguage.prefix
|
||||
suffix = tempInjectedLanguage.suffix
|
||||
}
|
||||
}
|
||||
|
||||
return findInjectionInfo(ktHost)?.toBaseInjection(support)
|
||||
}
|
||||
|
||||
override fun elementsToInjectIn(): List<Class<out PsiElement>> {
|
||||
return listOf(KtStringTemplateExpression::class.java)
|
||||
}
|
||||
|
||||
private fun findInjectionInfo(place: KtElement, originalHost: Boolean = true): InjectionInfo? {
|
||||
return injectWithExplicitCodeInstruction(place)
|
||||
?: injectWithCall(place)
|
||||
?: injectReturnValue(place)
|
||||
?: injectWithReceiver(place)
|
||||
?: injectWithVariableUsage(place, originalHost)
|
||||
?: injectWithAnnotationEntry(place)
|
||||
}
|
||||
|
||||
private fun injectReturnValue(place: KtElement): InjectionInfo? {
|
||||
val parent = place.parent
|
||||
|
||||
tailrec fun findReturnExpression(expression: PsiElement?): KtReturnExpression? = when (expression) {
|
||||
is KtReturnExpression -> expression
|
||||
is KtBinaryExpression -> findReturnExpression(expression.takeIf { it.operationToken == KtTokens.ELVIS }?.parent)
|
||||
is KtContainerNodeForControlStructureBody, is KtIfExpression -> findReturnExpression(expression.parent)
|
||||
else -> null
|
||||
}
|
||||
|
||||
val returnExp = findReturnExpression(parent) ?: return null
|
||||
|
||||
if (returnExp.labeledExpression != null) return null
|
||||
|
||||
val callableDeclaration = PsiTreeUtil.getParentOfType(returnExp, KtDeclaration::class.java) as? KtCallableDeclaration ?: return null
|
||||
if (callableDeclaration.annotationEntries.isEmpty()) return null
|
||||
|
||||
val descriptor = callableDeclaration.descriptor ?: return null
|
||||
return injectionInfoByAnnotation(descriptor)
|
||||
}
|
||||
|
||||
private fun injectWithExplicitCodeInstruction(host: KtElement): InjectionInfo? {
|
||||
val support = kotlinSupport ?: return null
|
||||
return InjectionInfo.fromBaseInjection(support.findCommentInjection(host)) ?: support.findAnnotationInjectionLanguageId(host)
|
||||
}
|
||||
|
||||
private fun injectWithReceiver(host: KtElement): InjectionInfo? {
|
||||
val qualifiedExpression = host.parent as? KtDotQualifiedExpression ?: return null
|
||||
if (qualifiedExpression.receiverExpression != host) return null
|
||||
|
||||
val callExpression = qualifiedExpression.selectorExpression as? KtCallExpression ?: return null
|
||||
val callee = callExpression.calleeExpression ?: return null
|
||||
|
||||
if (isAnalyzeOff(qualifiedExpression.project)) return null
|
||||
|
||||
val kotlinInjections = Configuration.getInstance().getInjections(KOTLIN_SUPPORT_ID)
|
||||
|
||||
val calleeName = callee.text
|
||||
val possibleNames = collectPossibleNames(kotlinInjections)
|
||||
|
||||
if (calleeName !in possibleNames) {
|
||||
return null
|
||||
}
|
||||
|
||||
for (reference in callee.references) {
|
||||
ProgressManager.checkCanceled()
|
||||
|
||||
val resolvedTo = reference.resolve()
|
||||
if (resolvedTo is KtFunction) {
|
||||
val injectionInfo = findInjection(resolvedTo.receiverTypeReference, kotlinInjections)
|
||||
if (injectionInfo != null) {
|
||||
return injectionInfo
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
private fun collectPossibleNames(injections: List<BaseInjection>): Set<String> {
|
||||
val result = HashSet<String>()
|
||||
|
||||
for (injection in injections) {
|
||||
val injectionPlaces = injection.injectionPlaces
|
||||
for (place in injectionPlaces) {
|
||||
val placeStr = place.toString()
|
||||
val literals = STRING_LITERALS_REGEXP.findAll(placeStr).map { it.groupValues[1] }
|
||||
result.addAll(literals)
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
private fun injectWithVariableUsage(host: KtElement, originalHost: Boolean): InjectionInfo? {
|
||||
// Given place is not original host of the injection so we stop to prevent stepping through indirect references
|
||||
if (!originalHost) return null
|
||||
|
||||
val ktHost: KtElement = host
|
||||
val ktProperty = host.parent as? KtProperty ?: return null
|
||||
if (ktProperty.initializer != host) return null
|
||||
|
||||
if (isAnalyzeOff(ktHost.project)) return null
|
||||
|
||||
val searchScope = LocalSearchScope(arrayOf(ktProperty.containingFile), "", true)
|
||||
return ReferencesSearch.search(ktProperty, searchScope).asSequence().mapNotNull { psiReference ->
|
||||
val element = psiReference.element as? KtElement ?: return@mapNotNull null
|
||||
findInjectionInfo(element, false)
|
||||
}.firstOrNull()
|
||||
}
|
||||
|
||||
private fun injectWithCall(host: KtElement): InjectionInfo? {
|
||||
val ktHost: KtElement = host
|
||||
val argument = ktHost.parent as? KtValueArgument ?: return null
|
||||
|
||||
val callExpression = PsiTreeUtil.getParentOfType(ktHost, KtCallExpression::class.java) ?: return null
|
||||
val callee = callExpression.calleeExpression ?: return null
|
||||
|
||||
if (isAnalyzeOff(ktHost.project)) return null
|
||||
|
||||
for (reference in callee.references) {
|
||||
ProgressManager.checkCanceled()
|
||||
|
||||
val resolvedTo = reference.resolve()
|
||||
if (resolvedTo is PsiMethod) {
|
||||
val injectionForJavaMethod = injectionForJavaMethod(argument, resolvedTo)
|
||||
if (injectionForJavaMethod != null) {
|
||||
return injectionForJavaMethod
|
||||
}
|
||||
}
|
||||
else if (resolvedTo is KtFunction) {
|
||||
val injectionForJavaMethod = injectionForKotlinCall(argument, resolvedTo, reference)
|
||||
if (injectionForJavaMethod != null) {
|
||||
return injectionForJavaMethod
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
private fun injectionForJavaMethod(argument: KtValueArgument, javaMethod: PsiMethod): InjectionInfo? {
|
||||
val argumentIndex = (argument.parent as KtValueArgumentList).arguments.indexOf(argument)
|
||||
val psiParameter = javaMethod.parameterList.parameters.getOrNull(argumentIndex) ?: return null
|
||||
|
||||
val injectionInfo = findInjection(psiParameter, Configuration.getInstance().getInjections(JavaLanguageInjectionSupport.JAVA_SUPPORT_ID))
|
||||
if (injectionInfo != null) {
|
||||
return injectionInfo
|
||||
}
|
||||
|
||||
val annotations = AnnotationUtilEx.getAnnotationFrom(
|
||||
psiParameter,
|
||||
Configuration.getProjectInstance(psiParameter.project).advancedConfiguration.languageAnnotationPair,
|
||||
true)
|
||||
|
||||
if (annotations.isNotEmpty()) {
|
||||
return processAnnotationInjectionInner(annotations)
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
private fun injectionForKotlinCall(argument: KtValueArgument, ktFunction: KtFunction, reference: PsiReference): InjectionInfo? {
|
||||
val argumentIndex = (argument.parent as KtValueArgumentList).arguments.indexOf(argument)
|
||||
val ktParameter = ktFunction.valueParameters.getOrNull(argumentIndex) ?: return null
|
||||
|
||||
val patternInjection = findInjection(ktParameter, Configuration.getInstance().getInjections(KOTLIN_SUPPORT_ID))
|
||||
if (patternInjection != null) {
|
||||
return patternInjection
|
||||
}
|
||||
|
||||
// Found psi element after resolve can be obtained from compiled declaration but annotations parameters are lost there.
|
||||
// Search for original descriptor from reference.
|
||||
val ktReference = reference as? KtReference ?: return null
|
||||
val bindingContext = ktReference.element.analyze(BodyResolveMode.PARTIAL_WITH_DIAGNOSTICS)
|
||||
val functionDescriptor = ktReference.resolveToDescriptors(bindingContext).singleOrNull() as? FunctionDescriptor ?: return null
|
||||
|
||||
val parameterDescriptor = functionDescriptor.valueParameters.getOrNull(argumentIndex) ?: return null
|
||||
return injectionInfoByAnnotation(parameterDescriptor)
|
||||
}
|
||||
|
||||
private fun injectionInfoByAnnotation(annotated: Annotated): InjectionInfo? {
|
||||
val injectAnnotation = annotated.annotations.findAnnotation(FqName(AnnotationUtil.LANGUAGE)) ?: return null
|
||||
|
||||
val languageId = injectAnnotation.argumentValue("value")?.safeAs<StringValue>()?.value ?: return null
|
||||
return InjectionInfo(languageId, null, null)
|
||||
}
|
||||
|
||||
private fun injectWithAnnotationEntry(host: KtElement): InjectionInfo? {
|
||||
val argument = host.parent as? KtValueArgument ?: return null
|
||||
val annotationEntry = PsiTreeUtil.getParentOfType(host, KtAnnotationEntry::class.java) ?: return null
|
||||
if (isAnalyzeOff(host.project)) return null
|
||||
|
||||
val calleeReference = annotationEntry.calleeExpression?.constructorReferenceExpression?.mainReference
|
||||
val callee = calleeReference?.resolve()
|
||||
return when (callee) {
|
||||
is KtFunction -> injectionForKotlinCall(argument, callee, calleeReference)
|
||||
is PsiClass -> {
|
||||
// Look for java injections for the PsiAnnotationMethod.
|
||||
(argument.reference ?: argument.getArgumentName()?.referenceExpression?.mainReference)
|
||||
?.let { it.resolve() as? PsiMethod }
|
||||
?.let { findInjection(it, Configuration.getInstance().getInjections(JavaLanguageInjectionSupport.JAVA_SUPPORT_ID)) }
|
||||
?.takeIf { injectionInfo ->
|
||||
// Temporary forbid injection for SpEL because of inspections that gives warnings when host language is not Java.
|
||||
injectionInfo.languageId != "SpEL"
|
||||
}
|
||||
}
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
private fun findInjection(element: PsiElement?, injections: List<BaseInjection>): InjectionInfo? {
|
||||
for (injection in injections) {
|
||||
if (injection.acceptsPsiElement(element)) {
|
||||
return InjectionInfo(injection.injectedLanguageId, injection.prefix, injection.suffix)
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
private fun isAnalyzeOff(project: Project): Boolean {
|
||||
return Configuration.getProjectInstance(project).advancedConfiguration.dfaOption == Configuration.DfaOption.OFF
|
||||
}
|
||||
|
||||
private fun processAnnotationInjectionInner(annotations: Array<PsiAnnotation>): InjectionInfo? {
|
||||
val id = AnnotationUtilEx.calcAnnotationValue(annotations, "value")
|
||||
val prefix = AnnotationUtilEx.calcAnnotationValue(annotations, "prefix")
|
||||
val suffix = AnnotationUtilEx.calcAnnotationValue(annotations, "suffix")
|
||||
|
||||
return InjectionInfo(id, prefix, suffix)
|
||||
}
|
||||
}
|
||||
-233
@@ -1,233 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.run;
|
||||
|
||||
import com.intellij.execution.JavaRunConfigurationExtensionManager;
|
||||
import com.intellij.execution.Location;
|
||||
import com.intellij.execution.PsiLocation;
|
||||
import com.intellij.execution.actions.ConfigurationContext;
|
||||
import com.intellij.execution.actions.ConfigurationFromContext;
|
||||
import com.intellij.execution.junit.InheritorChooser;
|
||||
import com.intellij.execution.junit2.info.MethodLocation;
|
||||
import com.intellij.openapi.module.Module;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.Condition;
|
||||
import com.intellij.openapi.util.Ref;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.util.PsiClassUtil;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.theoryinpractice.testng.configuration.TestNGConfiguration;
|
||||
import com.theoryinpractice.testng.configuration.TestNGConfigurationProducer;
|
||||
import com.theoryinpractice.testng.util.TestNGUtil;
|
||||
import kotlin.collections.CollectionsKt;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.kotlin.asJava.LightClassUtilsKt;
|
||||
import org.jetbrains.kotlin.idea.project.TargetPlatformDetector;
|
||||
import org.jetbrains.kotlin.idea.util.ProjectRootsUtil;
|
||||
import org.jetbrains.kotlin.psi.*;
|
||||
import org.jetbrains.kotlin.resolve.jvm.platform.JvmPlatform;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static org.jetbrains.kotlin.asJava.LightClassUtilsKt.toLightClass;
|
||||
|
||||
public class KotlinTestNgConfigurationProducer extends TestNGConfigurationProducer {
|
||||
@Override
|
||||
public boolean shouldReplace(ConfigurationFromContext self, ConfigurationFromContext other) {
|
||||
return other.isProducedBy(TestNGConfigurationProducer.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean setupConfigurationFromContext(
|
||||
TestNGConfiguration configuration, ConfigurationContext context, Ref<PsiElement> sourceElement
|
||||
) {
|
||||
// TODO: check TestNG Pattern running first, before method/class (see TestNGInClassConfigurationProducer for logic)
|
||||
// TODO: and PsiClassOwner not handled, which is in TestNGInClassConfigurationProducer
|
||||
|
||||
Location location = context.getLocation();
|
||||
if (location == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Project project = context.getProject();
|
||||
PsiElement leaf = location.getPsiElement();
|
||||
|
||||
if (!ProjectRootsUtil.isInProjectOrLibSource(leaf)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!(leaf.getContainingFile() instanceof KtFile)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
KtFile ktFile = (KtFile) leaf.getContainingFile();
|
||||
|
||||
if (TargetPlatformDetector.getPlatform(ktFile) != JvmPlatform.INSTANCE) {
|
||||
return false;
|
||||
}
|
||||
|
||||
KtNamedDeclaration declarationToRun = getDeclarationToRun(leaf);
|
||||
|
||||
if (declarationToRun instanceof KtNamedFunction) {
|
||||
KtNamedFunction function = (KtNamedFunction) declarationToRun;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
KtElement owner = PsiTreeUtil.getParentOfType(function, KtFunction.class, KtClass.class);
|
||||
|
||||
if (owner instanceof KtClass) {
|
||||
PsiClass delegate = toLightClass((KtClass) owner);
|
||||
if (delegate != null) {
|
||||
for (PsiMethod method : delegate.getMethods()) {
|
||||
if (method.getNavigationElement() == function) {
|
||||
if (TestNGUtil.hasTest(method)) {
|
||||
return configure(configuration, location, context, project, delegate, method);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (declarationToRun instanceof KtClass) {
|
||||
PsiClass delegate = toLightClass((KtClassOrObject) declarationToRun);
|
||||
if (!isTestNGClass(delegate)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return configure(configuration, location, context, project, delegate, null);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFirstRun(ConfigurationFromContext configuration, ConfigurationContext context, Runnable startRunnable) {
|
||||
KtNamedDeclaration declarationToRun = getDeclarationToRun(configuration.getSourceElement());
|
||||
final PsiNamedElement lightElement = CollectionsKt.firstOrNull(LightClassUtilsKt.toLightElements(declarationToRun));
|
||||
|
||||
// Copied from TestNGInClassConfigurationProducer.onFirstRun()
|
||||
if (lightElement instanceof PsiMethod || lightElement instanceof PsiClass) {
|
||||
PsiMethod psiMethod;
|
||||
PsiClass containingClass;
|
||||
|
||||
if (lightElement instanceof PsiMethod) {
|
||||
psiMethod = (PsiMethod)lightElement;
|
||||
containingClass = psiMethod.getContainingClass();
|
||||
} else {
|
||||
psiMethod = null;
|
||||
containingClass = (PsiClass)lightElement;
|
||||
}
|
||||
|
||||
InheritorChooser inheritorChooser = new InheritorChooser() {
|
||||
@Override
|
||||
protected void runForClasses(List<PsiClass> classes, PsiMethod method, ConfigurationContext context, Runnable performRunnable) {
|
||||
((TestNGConfiguration)context.getConfiguration().getConfiguration()).bePatternConfiguration(classes, method);
|
||||
super.runForClasses(classes, method, context, performRunnable);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void runForClass(PsiClass aClass,
|
||||
PsiMethod psiMethod,
|
||||
ConfigurationContext context,
|
||||
Runnable performRunnable) {
|
||||
if (lightElement instanceof PsiMethod) {
|
||||
Project project = psiMethod.getProject();
|
||||
MethodLocation methodLocation = new MethodLocation(project, psiMethod, PsiLocation.fromPsiElement(aClass));
|
||||
((TestNGConfiguration)context.getConfiguration().getConfiguration()).setMethodConfiguration(methodLocation);
|
||||
} else {
|
||||
((TestNGConfiguration)context.getConfiguration().getConfiguration()).setClassConfiguration(aClass);
|
||||
}
|
||||
super.runForClass(aClass, psiMethod, context, performRunnable);
|
||||
}
|
||||
};
|
||||
if (inheritorChooser.runMethodInAbstractClass(context,
|
||||
startRunnable,
|
||||
psiMethod,
|
||||
containingClass,
|
||||
new Condition<PsiClass>() {
|
||||
@Override
|
||||
public boolean value(PsiClass aClass) {
|
||||
return aClass.hasModifierProperty(PsiModifier.ABSTRACT) &&
|
||||
TestNGUtil.hasTest(aClass);
|
||||
}
|
||||
})) return;
|
||||
}
|
||||
|
||||
super.onFirstRun(configuration, context, startRunnable);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static KtNamedDeclaration getDeclarationToRun(@NotNull PsiElement leaf) {
|
||||
if (!(leaf.getContainingFile() instanceof KtFile)) return null;
|
||||
KtFile jetFile = (KtFile) leaf.getContainingFile();
|
||||
|
||||
KtNamedFunction function = PsiTreeUtil.getParentOfType(leaf, KtNamedFunction.class, false);
|
||||
if (function != null) return function;
|
||||
|
||||
KtClass ktClass = PsiTreeUtil.getParentOfType(leaf, KtClass.class, false);
|
||||
if (ktClass != null) return ktClass;
|
||||
|
||||
return getClassDeclarationInFile(jetFile);
|
||||
}
|
||||
|
||||
private boolean configure(
|
||||
TestNGConfiguration configuration, Location location, ConfigurationContext context, Project project,
|
||||
@Nullable PsiClass delegate, @Nullable PsiMethod method
|
||||
) {
|
||||
if (delegate == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
setupConfigurationModule(context, configuration);
|
||||
Module originalModule = configuration.getConfigurationModule().getModule();
|
||||
configuration.setClassConfiguration(delegate);
|
||||
if (method != null) {
|
||||
configuration.setMethodConfiguration(PsiLocation.fromPsiElement(project, method));
|
||||
}
|
||||
configuration.restoreOriginalModule(originalModule);
|
||||
configuration.setName(configuration.getName());
|
||||
JavaRunConfigurationExtensionManager.getInstance().extendCreatedConfiguration(configuration, location);
|
||||
return true;
|
||||
}
|
||||
|
||||
private static boolean isTestNGClass(PsiClass psiClass) {
|
||||
return psiClass != null && PsiClassUtil.isRunnableClass(psiClass, true, false) && TestNGUtil.hasTest(psiClass);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
static KtClass getClassDeclarationInFile(KtFile jetFile) {
|
||||
KtClass tempSingleDeclaration = null;
|
||||
|
||||
for (KtDeclaration ktDeclaration : jetFile.getDeclarations()) {
|
||||
if (ktDeclaration instanceof KtClass) {
|
||||
KtClass declaration = (KtClass) ktDeclaration;
|
||||
|
||||
if (tempSingleDeclaration == null) {
|
||||
tempSingleDeclaration = declaration;
|
||||
}
|
||||
else {
|
||||
// There are several class declarations in file
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return tempSingleDeclaration;
|
||||
}
|
||||
}
|
||||
-78
@@ -1,78 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2018 JetBrains s.r.o. 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.scratch.output
|
||||
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.intellij.openapi.editor.EditorCustomElementRenderer
|
||||
import com.intellij.openapi.editor.impl.ComplementaryFontsRegistry
|
||||
import com.intellij.openapi.editor.impl.FontInfo
|
||||
import com.intellij.openapi.editor.markup.TextAttributes
|
||||
import com.intellij.ui.Colors
|
||||
import com.intellij.ui.JBColor
|
||||
import java.awt.Color
|
||||
import java.awt.Font
|
||||
import java.awt.Graphics
|
||||
import java.awt.Rectangle
|
||||
|
||||
class InlayScratchFileRenderer(val text: String, private val outputType: ScratchOutputType) : EditorCustomElementRenderer {
|
||||
private fun getFontInfo(editor: Editor): FontInfo {
|
||||
val colorsScheme = editor.colorsScheme
|
||||
val fontPreferences = colorsScheme.fontPreferences
|
||||
val attributes = getAttributes()
|
||||
val fontStyle = attributes.fontType
|
||||
return ComplementaryFontsRegistry.getFontAbleToDisplay(
|
||||
'a'.toInt(), fontStyle, fontPreferences, FontInfo.getFontRenderContext(editor.contentComponent)
|
||||
)
|
||||
}
|
||||
|
||||
override fun calcWidthInPixels(editor: Editor): Int {
|
||||
val fontInfo = getFontInfo(editor)
|
||||
return fontInfo.fontMetrics().stringWidth(text)
|
||||
}
|
||||
|
||||
override fun paint(editor: Editor, g: Graphics, r: Rectangle) {
|
||||
val attributes = getAttributes()
|
||||
val fgColor = attributes.foregroundColor ?: return
|
||||
g.color = fgColor
|
||||
val fontInfo = getFontInfo(editor)
|
||||
g.font = fontInfo.font
|
||||
val metrics = fontInfo.fontMetrics()
|
||||
g.drawString(text, r.x, r.y + metrics.ascent)
|
||||
}
|
||||
|
||||
private fun getAttributes(): TextAttributes {
|
||||
return when (outputType) {
|
||||
ScratchOutputType.OUTPUT -> userOutputAttributes
|
||||
ScratchOutputType.RESULT -> normalAttributes
|
||||
ScratchOutputType.ERROR -> errorAttributes
|
||||
}
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return "${text.takeWhile { it.isWhitespace() }}${outputType.name}: ${text.trim()}"
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val normalAttributes = TextAttributes(
|
||||
JBColor.GRAY,
|
||||
null, null, null,
|
||||
Font.ITALIC
|
||||
)
|
||||
|
||||
private val errorAttributes = TextAttributes(
|
||||
JBColor(Colors.DARK_RED, Colors.DARK_RED),
|
||||
null, null, null,
|
||||
Font.ITALIC
|
||||
)
|
||||
|
||||
private val userOutputColor = Color(0x5C5CFF)
|
||||
private val userOutputAttributes = TextAttributes(
|
||||
JBColor(userOutputColor, userOutputColor),
|
||||
null, null, null,
|
||||
Font.ITALIC
|
||||
)
|
||||
}
|
||||
}
|
||||
-190
@@ -1,190 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2016 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.maven
|
||||
|
||||
import com.intellij.codeInspection.CommonProblemDescriptor
|
||||
import com.intellij.codeInspection.ProblemDescriptorBase
|
||||
import com.intellij.codeInspection.QuickFix
|
||||
import com.intellij.ide.highlighter.JavaFileType
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.application.Result
|
||||
import com.intellij.openapi.application.WriteAction
|
||||
import com.intellij.openapi.command.CommandProcessor
|
||||
import com.intellij.openapi.fileEditor.FileDocumentManager
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import com.intellij.psi.PsiDocumentManager
|
||||
import com.intellij.psi.PsiManager
|
||||
import com.intellij.psi.search.FileTypeIndex
|
||||
import org.jetbrains.jps.model.java.JavaSourceRootType
|
||||
import org.jetbrains.kotlin.idea.inspections.runInspection
|
||||
import org.jetbrains.kotlin.idea.maven.inspections.KotlinMavenPluginPhaseInspection
|
||||
import org.jetbrains.kotlin.idea.refactoring.toPsiDirectory
|
||||
import org.jetbrains.kotlin.idea.util.projectStructure.allModules
|
||||
import org.jetbrains.kotlin.test.KotlinTestUtils
|
||||
import org.jetbrains.kotlin.utils.keysToMap
|
||||
import java.io.File
|
||||
|
||||
abstract class AbstractKotlinMavenInspectionTest : MavenImportingTestCase() {
|
||||
|
||||
override fun setUp() {
|
||||
super.setUp()
|
||||
repositoryPath = File(myDir, "repo").path
|
||||
createStdProjectFolders()
|
||||
}
|
||||
|
||||
fun doTest(fileName: String) {
|
||||
val pomFile = File(fileName)
|
||||
val pomText = pomFile.readText()
|
||||
|
||||
createPomFile(fileName)
|
||||
importProject()
|
||||
myProject.allModules().forEach {
|
||||
setupJdkForModule(it.name)
|
||||
}
|
||||
|
||||
if (pomText.contains("<!--\\s*mkjava\\s*-->".toRegex(RegexOption.MULTILINE))) {
|
||||
mkJavaFile()
|
||||
}
|
||||
|
||||
val inspectionClassName = "<!--\\s*inspection:\\s*([\\S]+)\\s-->".toRegex().find(pomText)?.groups?.get(1)?.value
|
||||
?: KotlinMavenPluginPhaseInspection::class.qualifiedName!!
|
||||
val inspectionClass = Class.forName(inspectionClassName)
|
||||
|
||||
val matcher = "<!--\\s*problem:\\s*on\\s*([^,]+),\\s*title\\s*(.+)\\s*-->".toRegex()
|
||||
val expectedProblemsText = pomText.lines()
|
||||
.filter { matcher.matches(it) }
|
||||
.joinToString("\n")
|
||||
|
||||
val problemElements = runInspection(inspectionClass, myProject).problemElements
|
||||
val actualProblems = problemElements
|
||||
.filter { it.key.name == "pom.xml" }
|
||||
.values
|
||||
.flatMap { it.toList() }
|
||||
.mapNotNull { it as? ProblemDescriptorBase }
|
||||
|
||||
val actual = actualProblems
|
||||
.map { SimplifiedProblemDescription(it.descriptionTemplate, it.psiElement.text.replace("\\s+".toRegex(), "")) to it }
|
||||
.sortedBy { it.first.text }
|
||||
|
||||
val actualProblemsText = actual
|
||||
.map { it.first }
|
||||
.joinToString("\n") { "<!-- problem: on ${it.elementText}, title ${it.text} -->"}
|
||||
|
||||
assertEquals(expectedProblemsText, actualProblemsText)
|
||||
|
||||
val suggestedFixes = actual.flatMap { p -> p.second.fixes?.sortedBy { it.familyName }?.map { p.second to it } ?: emptyList() }
|
||||
|
||||
val filenamePrefix = pomFile.nameWithoutExtension + ".fixed."
|
||||
val fixFiles =
|
||||
pomFile.parentFile.listFiles { _, name -> name.startsWith(filenamePrefix) && name.endsWith(".xml") }.sortedBy { it.name }
|
||||
|
||||
val rangesToFixFiles: Map<File, IntRange> = fixFiles.keysToMap {
|
||||
val fixFileName = it.name
|
||||
val fixRangeStr = fixFileName.substringBeforeLast('.').substringAfterLast('.')
|
||||
val numbers = fixRangeStr.split('-').map { it.toInt() }
|
||||
when (numbers.size) {
|
||||
0 -> error("No number in fix file $fixFileName")
|
||||
1 -> IntRange(numbers[0], numbers[0])
|
||||
2 -> IntRange(numbers[0], numbers[1])
|
||||
else -> error("Bad range `$fixRangeStr` in fix file $fixFileName")
|
||||
}
|
||||
}
|
||||
|
||||
val sortedFixRanges = rangesToFixFiles.values.sortedBy { it.start }
|
||||
sortedFixRanges.forEachIndexed { i, range ->
|
||||
if (i > 0) {
|
||||
val previous = sortedFixRanges[i - 1]
|
||||
if (previous.endInclusive + 1 != range.start) {
|
||||
error("Bad ranges in fix files: $previous and $range")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val numberOfFixDataFiles = sortedFixRanges.lastOrNull()?.endInclusive ?: 0
|
||||
if (numberOfFixDataFiles > suggestedFixes.size) {
|
||||
fail("Not all fixes were suggested by the inspection: expected count: ${fixFiles.size}, actual fixes count: ${suggestedFixes.size}")
|
||||
}
|
||||
if (numberOfFixDataFiles < suggestedFixes.size) {
|
||||
fail("Not all fixes covered by *.fixed.N.xml files")
|
||||
}
|
||||
|
||||
val documentManager = PsiDocumentManager.getInstance(myProject)
|
||||
val document = documentManager.getDocument(PsiManager.getInstance(myProject).findFile(myProjectPom)!!)!!
|
||||
val originalText = document.text
|
||||
|
||||
suggestedFixes.forEachIndexed { index, suggestedFix ->
|
||||
val (problem, quickfix) = suggestedFix
|
||||
val file = rangesToFixFiles.entries.first { (_, range) -> index + 1 in range }.key
|
||||
|
||||
quickfix.applyFix(problem)
|
||||
|
||||
KotlinTestUtils.assertEqualsToFile(file, document.text.trim())
|
||||
|
||||
ApplicationManager.getApplication().runWriteAction {
|
||||
document.setText(originalText)
|
||||
documentManager.commitDocument(document)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun createPomFile(fileName: String) {
|
||||
myProjectPom = myProjectRoot.findChild("pom.xml")
|
||||
if (myProjectPom == null) {
|
||||
myProjectPom = object : WriteAction<VirtualFile>() {
|
||||
override fun run(result: Result<VirtualFile>) {
|
||||
val res = myProjectRoot.createChildData(null, "pom.xml")
|
||||
result.setResult(res)
|
||||
}
|
||||
}.execute().resultObject
|
||||
}
|
||||
myAllPoms.add(myProjectPom!!)
|
||||
|
||||
ApplicationManager.getApplication().runWriteAction {
|
||||
myProjectPom!!.setBinaryContent(File(fileName).readBytes())
|
||||
}
|
||||
}
|
||||
|
||||
private fun QuickFix<CommonProblemDescriptor>.applyFix(desc: ProblemDescriptorBase) {
|
||||
CommandProcessor.getInstance().executeCommand(myProject, {
|
||||
ApplicationManager.getApplication().runWriteAction {
|
||||
applyFix(myProject, desc)
|
||||
|
||||
val manager = PsiDocumentManager.getInstance(myProject)
|
||||
val document = manager.getDocument(PsiManager.getInstance(myProject).findFile(myProjectPom)!!)!!
|
||||
manager.doPostponedOperationsAndUnblockDocument(document)
|
||||
manager.commitDocument(document)
|
||||
FileDocumentManager.getInstance().saveDocument(document)
|
||||
|
||||
}
|
||||
|
||||
println(myProjectPom.contentsToByteArray().toString(Charsets.UTF_8))
|
||||
}, "quick-fix-$name", "Kotlin")
|
||||
}
|
||||
|
||||
private fun mkJavaFile() {
|
||||
val sourceFolder =
|
||||
getContentRoots(myProject.allModules().single().name).single().getSourceFolders(JavaSourceRootType.SOURCE).single()
|
||||
ApplicationManager.getApplication().runWriteAction {
|
||||
val javaFile = sourceFolder.file?.toPsiDirectory(myProject)?.createFile("Test.java") ?: throw IllegalStateException()
|
||||
javaFile.viewProvider.document!!.setText("class Test {}\n")
|
||||
}
|
||||
|
||||
assertTrue(FileTypeIndex.containsFileOfType(JavaFileType.INSTANCE, myProject.allModules().single().moduleScope))
|
||||
}
|
||||
|
||||
private data class SimplifiedProblemDescription(val text: String, val elementText: String)
|
||||
}
|
||||
@@ -1,138 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2016 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.console
|
||||
|
||||
import com.intellij.execution.process.OSProcessHandler
|
||||
import com.intellij.execution.process.ProcessOutputTypes
|
||||
import com.intellij.openapi.util.Key
|
||||
import com.intellij.openapi.util.TextRange
|
||||
import com.intellij.openapi.util.text.StringUtil
|
||||
import org.jetbrains.kotlin.console.actions.logError
|
||||
import org.jetbrains.kotlin.diagnostics.Severity
|
||||
import org.jetbrains.kotlin.utils.repl.ReplEscapeType
|
||||
import org.w3c.dom.Element
|
||||
import org.xml.sax.InputSource
|
||||
import java.io.ByteArrayInputStream
|
||||
import java.nio.charset.Charset
|
||||
import javax.xml.parsers.DocumentBuilderFactory
|
||||
import org.jetbrains.kotlin.utils.repl.ReplEscapeType.*
|
||||
|
||||
val XML_REPLACEMENTS: Array<String> = arrayOf("#n", "#diez")
|
||||
val SOURCE_CHARS: Array<String> = arrayOf("\n", "#")
|
||||
|
||||
data class SeverityDetails(val severity: Severity, val description: String, val range: TextRange)
|
||||
|
||||
class ReplOutputHandler(
|
||||
private val runner: KotlinConsoleRunner,
|
||||
process: Process,
|
||||
commandLine: String
|
||||
) : OSProcessHandler(process, commandLine) {
|
||||
|
||||
private var isBuildInfoChecked = false
|
||||
private val factory = DocumentBuilderFactory.newInstance()
|
||||
private val outputProcessor = ReplOutputProcessor(runner)
|
||||
private val inputBuffer = StringBuilder()
|
||||
|
||||
override fun isSilentlyDestroyOnClose() = true
|
||||
|
||||
override fun notifyTextAvailable(text: String, key: Key<*>?) {
|
||||
// hide warning about adding test folder to classpath
|
||||
if (text.startsWith("warning: classpath entry points to a non-existent location")) return
|
||||
|
||||
if (key == ProcessOutputTypes.STDOUT) {
|
||||
inputBuffer.append(text)
|
||||
val resultingText = inputBuffer.toString()
|
||||
if (resultingText.endsWith("\n")) {
|
||||
handleReplMessage(resultingText)
|
||||
inputBuffer.setLength(0)
|
||||
}
|
||||
}
|
||||
else {
|
||||
super.notifyTextAvailable(text, key)
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleReplMessage(text: String) {
|
||||
if (text.isBlank()) return
|
||||
val output = try {
|
||||
factory.newDocumentBuilder().parse(strToSource(text))
|
||||
}
|
||||
catch (e: Exception) {
|
||||
logError(ReplOutputHandler::class.java, "Couldn't parse REPL output: $text", e)
|
||||
return
|
||||
}
|
||||
|
||||
val root = output.firstChild as Element
|
||||
val outputType = ReplEscapeType.valueOfOrNull(root.getAttribute("type"))
|
||||
val content = StringUtil.replace(root.textContent, XML_REPLACEMENTS, SOURCE_CHARS)
|
||||
|
||||
when (outputType) {
|
||||
INITIAL_PROMPT -> buildWarningIfNeededBeforeInit(content)
|
||||
HELP_PROMPT -> outputProcessor.printHelp(content)
|
||||
USER_OUTPUT -> outputProcessor.printUserOutput(content)
|
||||
REPL_RESULT -> outputProcessor.printResultWithGutterIcon(content)
|
||||
READLINE_START -> runner.isReadLineMode = true
|
||||
READLINE_END -> runner.isReadLineMode = false
|
||||
REPL_INCOMPLETE,
|
||||
COMPILE_ERROR -> outputProcessor.highlightCompilerErrors(createCompilerMessages(content))
|
||||
RUNTIME_ERROR -> outputProcessor.printRuntimeError("${content.trim()}\n")
|
||||
INTERNAL_ERROR -> outputProcessor.printInternalErrorMessage(content)
|
||||
SUCCESS -> runner.commandHistory.lastUnprocessedEntry()?.entryText?.let { runner.successfulLine(it) }
|
||||
null -> logError(ReplOutputHandler::class.java, "Unexpected output type:\n$outputType")
|
||||
}
|
||||
|
||||
if (outputType in setOf(SUCCESS, COMPILE_ERROR, INTERNAL_ERROR, RUNTIME_ERROR, READLINE_END)) {
|
||||
runner.commandHistory.entryProcessed()
|
||||
}
|
||||
}
|
||||
|
||||
private fun buildWarningIfNeededBeforeInit(content: String) {
|
||||
if (!isBuildInfoChecked) {
|
||||
outputProcessor.printBuildInfoWarningIfNeeded()
|
||||
isBuildInfoChecked = true
|
||||
}
|
||||
outputProcessor.printInitialPrompt(content)
|
||||
}
|
||||
|
||||
private fun strToSource(s: String, encoding: Charset = Charsets.UTF_8) = InputSource(ByteArrayInputStream(s.toByteArray(encoding)))
|
||||
|
||||
private fun createCompilerMessages(runtimeErrorsReport: String): List<SeverityDetails> {
|
||||
val compilerMessages = arrayListOf<SeverityDetails>()
|
||||
|
||||
val report = factory.newDocumentBuilder().parse(strToSource(runtimeErrorsReport, Charsets.UTF_16))
|
||||
val entries = report.getElementsByTagName("reportEntry")
|
||||
for (i in 0..entries.length - 1) {
|
||||
val reportEntry = entries.item(i) as Element
|
||||
|
||||
val severityLevel = reportEntry.getAttribute("severity").toSeverity()
|
||||
val rangeStart = reportEntry.getAttribute("rangeStart").toInt()
|
||||
val rangeEnd = reportEntry.getAttribute("rangeEnd").toInt()
|
||||
val description = reportEntry.textContent
|
||||
|
||||
compilerMessages.add(SeverityDetails(severityLevel, description, TextRange(rangeStart, rangeEnd)))
|
||||
}
|
||||
|
||||
return compilerMessages
|
||||
}
|
||||
|
||||
private fun String.toSeverity() = when (this) {
|
||||
"ERROR" -> Severity.ERROR
|
||||
"WARNING" -> Severity.WARNING
|
||||
"INFO" -> Severity.INFO
|
||||
else -> throw IllegalArgumentException("Unsupported Severity: '$this'") // this case shouldn't occur
|
||||
}
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
<idea-plugin>
|
||||
<id>org.jetbrains.kotlin</id>
|
||||
|
||||
<extensionPoints>
|
||||
<extensionPoint qualifiedName="org.jetbrains.kotlin.diagnosticSuppressor"
|
||||
interface="org.jetbrains.kotlin.resolve.diagnostics.DiagnosticSuppressor"/>
|
||||
<extensionPoint qualifiedName="org.jetbrains.kotlin.expressionCodegenExtension"
|
||||
interface="org.jetbrains.kotlin.codegen.extensions.ExpressionCodegenExtension"
|
||||
area="IDEA_PROJECT"/>
|
||||
<extensionPoint qualifiedName="org.jetbrains.kotlin.syntheticResolveExtension"
|
||||
interface="org.jetbrains.kotlin.resolve.extensions.SyntheticResolveExtension"
|
||||
area="IDEA_PROJECT"/>
|
||||
<extensionPoint qualifiedName="org.jetbrains.kotlin.jsSyntheticTranslateExtension"
|
||||
interface="org.jetbrains.kotlin.js.translate.extensions.JsSyntheticTranslateExtension"
|
||||
area="IDEA_PROJECT"/>
|
||||
<extensionPoint qualifiedName="org.jetbrains.kotlin.simpleNameReferenceExtension"
|
||||
interface="org.jetbrains.kotlin.plugin.references.SimpleNameReferenceExtension"
|
||||
area="IDEA_PROJECT"/>
|
||||
<extensionPoint qualifiedName="org.jetbrains.kotlin.kotlinIndicesHelperExtension"
|
||||
interface="org.jetbrains.kotlin.idea.core.extension.KotlinIndicesHelperExtension"
|
||||
area="IDEA_PROJECT"/>
|
||||
<extensionPoint qualifiedName="org.jetbrains.kotlin.classBuilderFactoryInterceptorExtension"
|
||||
interface="org.jetbrains.kotlin.codegen.extensions.ClassBuilderInterceptorExtension"
|
||||
area="IDEA_PROJECT"/>
|
||||
<extensionPoint qualifiedName="org.jetbrains.kotlin.packageFragmentProviderExtension"
|
||||
interface="org.jetbrains.kotlin.resolve.jvm.extensions.PackageFragmentProviderExtension"
|
||||
area="IDEA_PROJECT"/>
|
||||
<extensionPoint qualifiedName="org.jetbrains.kotlin.quickFixContributor"
|
||||
interface="org.jetbrains.kotlin.idea.quickfix.QuickFixContributor"/>
|
||||
<extensionPoint qualifiedName="org.jetbrains.kotlin.storageComponentContainerContributor"
|
||||
interface="org.jetbrains.kotlin.extensions.StorageComponentContainerContributor"
|
||||
area="IDEA_PROJECT"/>
|
||||
<extensionPoint qualifiedName="org.jetbrains.kotlin.scriptDefinitionContributor"
|
||||
interface="org.jetbrains.kotlin.idea.core.script.ScriptDefinitionContributor"
|
||||
area="IDEA_PROJECT"/>
|
||||
<extensionPoint qualifiedName="org.jetbrains.kotlin.scriptTemplatesProvider"
|
||||
interface="org.jetbrains.kotlin.script.ScriptTemplatesProvider"
|
||||
area="IDEA_PROJECT"/>
|
||||
<extensionPoint qualifiedName="org.jetbrains.kotlin.facetConfigurationExtension"
|
||||
interface="org.jetbrains.kotlin.idea.facet.KotlinFacetConfigurationExtension"/>
|
||||
<extensionPoint qualifiedName="org.jetbrains.kotlin.versionInfoProvider"
|
||||
interface="org.jetbrains.kotlin.idea.facet.KotlinVersionInfoProvider"/>
|
||||
<extensionPoint qualifiedName="org.jetbrains.kotlin.completionInformationProvider"
|
||||
interface="org.jetbrains.kotlin.idea.completion.CompletionInformationProvider" />
|
||||
<extensionPoint qualifiedName="org.jetbrains.kotlin.moduleBuilder"
|
||||
interface="com.intellij.ide.util.projectWizard.ModuleBuilder"/>
|
||||
<extensionPoint qualifiedName="org.jetbrains.kotlin.renameHandler"
|
||||
interface="com.intellij.refactoring.rename.RenameHandler"/>
|
||||
</extensionPoints>
|
||||
</idea-plugin>
|
||||
@@ -1,98 +0,0 @@
|
||||
<idea-plugin>
|
||||
<extensions defaultExtensionNs="org.jetbrains.plugins.gradle">
|
||||
<frameworkSupport implementation="org.jetbrains.kotlin.idea.configuration.GradleKotlinJavaFrameworkSupportProvider"/>
|
||||
<frameworkSupport implementation="org.jetbrains.kotlin.idea.configuration.GradleKotlinJSFrameworkSupportProvider"/>
|
||||
<frameworkSupport implementation="org.jetbrains.kotlin.idea.configuration.GradleKotlinMPPCommonFrameworkSupportProvider"/>
|
||||
<frameworkSupport implementation="org.jetbrains.kotlin.idea.configuration.GradleKotlinMPPJavaFrameworkSupportProvider"/>
|
||||
<frameworkSupport implementation="org.jetbrains.kotlin.idea.configuration.GradleKotlinMPPJSFrameworkSupportProvider"/>
|
||||
<pluginDescriptions implementation="org.jetbrains.kotlin.idea.configuration.KotlinGradlePluginDescription"/>
|
||||
<projectResolve implementation="org.jetbrains.kotlin.idea.configuration.KotlinGradleProjectResolverExtension" order="first"/>
|
||||
<projectResolve implementation="org.jetbrains.kotlin.kapt.idea.KaptProjectResolverExtension" order="last"/>
|
||||
<projectResolve implementation="org.jetbrains.kotlin.allopen.ide.AllOpenProjectResolverExtension" order="last"/>
|
||||
<projectResolve implementation="org.jetbrains.kotlin.noarg.ide.NoArgProjectResolverExtension" order="last"/>
|
||||
<projectResolve implementation="org.jetbrains.kotlin.samWithReceiver.ide.SamWithReceiverProjectResolverExtension" order="last"/>
|
||||
</extensions>
|
||||
|
||||
<extensionPoints>
|
||||
<extensionPoint qualifiedName="org.jetbrains.kotlin.gradleProjectImportHandler" area="IDEA_PROJECT"
|
||||
interface="org.jetbrains.kotlin.idea.configuration.GradleProjectImportHandler"/>
|
||||
|
||||
<extensionPoint qualifiedName="org.jetbrains.kotlin.gradleModelFacade"
|
||||
interface="org.jetbrains.kotlin.idea.inspections.gradle.KotlinGradleModelFacade"/>
|
||||
|
||||
<extensionPoint qualifiedName="org.jetbrains.kotlin.gradleFrameworkSupport"
|
||||
interface="org.jetbrains.kotlin.gradle.kdsl.frameworkSupport.GradleFrameworkSupportProvider" />
|
||||
</extensionPoints>
|
||||
|
||||
<extensions defaultExtensionNs="com.intellij">
|
||||
<externalProjectDataService implementation="org.jetbrains.kotlin.idea.configuration.KotlinGradleSourceSetDataService"/>
|
||||
<externalProjectDataService implementation="org.jetbrains.kotlin.idea.configuration.KotlinGradleProjectDataService"/>
|
||||
<externalProjectDataService implementation="org.jetbrains.kotlin.idea.configuration.KotlinGradleLibraryDataService"/>
|
||||
<externalSystemTaskNotificationListener implementation="org.jetbrains.kotlin.idea.core.script.ReloadGradleTemplatesOnSync"/>
|
||||
|
||||
<localInspection
|
||||
implementationClass="org.jetbrains.kotlin.idea.inspections.gradle.DifferentKotlinGradleVersionInspection"
|
||||
displayName="Kotlin Gradle and IDE plugins versions are different"
|
||||
groupName="Kotlin"
|
||||
enabledByDefault="true"
|
||||
language="Groovy"
|
||||
hasStaticDescription="true"
|
||||
level="WARNING"/>
|
||||
|
||||
<localInspection
|
||||
implementationClass="org.jetbrains.kotlin.idea.inspections.gradle.DifferentStdlibGradleVersionInspection"
|
||||
displayName="Kotlin library and Gradle plugin versions are different"
|
||||
groupName="Kotlin"
|
||||
enabledByDefault="true"
|
||||
language="Groovy"
|
||||
hasStaticDescription="true"
|
||||
level="WARNING"/>
|
||||
|
||||
<localInspection
|
||||
implementationClass="org.jetbrains.kotlin.idea.inspections.gradle.DeprecatedGradleDependencyInspection"
|
||||
displayName="Deprecated library is used in Gradle"
|
||||
groupName="Kotlin"
|
||||
enabledByDefault="true"
|
||||
language="Groovy"
|
||||
hasStaticDescription="true"
|
||||
level="WARNING"/>
|
||||
|
||||
<localInspection
|
||||
implementationClass="org.jetbrains.kotlin.idea.inspections.gradle.GradleKotlinxCoroutinesDeprecationInspection"
|
||||
displayName="kotlinx.coroutines dependencies should be updated in Gradle for using with Kotlin 1.3+"
|
||||
groupPath="Kotlin,Migration"
|
||||
groupName="Gradle"
|
||||
enabledByDefault="true"
|
||||
language="Groovy"
|
||||
hasStaticDescription="true"
|
||||
level="ERROR"/>
|
||||
|
||||
<runConfigurationProducer implementation="org.jetbrains.kotlin.idea.run.KotlinTestClassGradleConfigurationProducer"/>
|
||||
<runConfigurationProducer implementation="org.jetbrains.kotlin.idea.run.KotlinTestMethodGradleConfigurationProducer"/>
|
||||
|
||||
<orderEnumerationHandlerFactory implementation="org.jetbrains.kotlin.android.AndroidGradleOrderEnumerationHandler$FactoryImpl" order="first"/>
|
||||
|
||||
<moduleBuilder builderClass="org.jetbrains.kotlin.gradle.kdsl.GradleModuleBuilder" />
|
||||
</extensions>
|
||||
|
||||
<extensions defaultExtensionNs="org.jetbrains.kotlin">
|
||||
<gradleProjectImportHandler implementation="org.jetbrains.kotlin.allopen.ide.AllOpenGradleProjectImportHandler"/>
|
||||
<gradleProjectImportHandler implementation="org.jetbrains.kotlin.scripting.idea.plugin.ScriptingGradleProjectImportHandler"/>
|
||||
<gradleProjectImportHandler implementation="org.jetbrains.kotlin.noarg.ide.NoArgGradleProjectImportHandler"/>
|
||||
<gradleProjectImportHandler implementation="org.jetbrains.kotlin.samWithReceiver.ide.SamWithReceiverGradleProjectImportHandler"/>
|
||||
|
||||
<projectConfigurator implementation="org.jetbrains.kotlin.idea.configuration.KotlinGradleModuleConfigurator"/>
|
||||
<projectConfigurator implementation="org.jetbrains.kotlin.idea.configuration.KotlinJsGradleModuleConfigurator"/>
|
||||
<gradleModelFacade implementation="org.jetbrains.kotlin.idea.inspections.gradle.DefaultGradleModelFacade"/>
|
||||
|
||||
<scriptDefinitionContributor implementation="org.jetbrains.kotlin.idea.core.script.GradleScriptDefinitionsContributor" order="first"/>
|
||||
|
||||
<moduleBuilder implementation="org.jetbrains.kotlin.idea.configuration.KotlinGradleMultiplatformModuleBuilder"/>
|
||||
|
||||
<gradleFrameworkSupport implementation="org.jetbrains.kotlin.gradle.kdsl.frameworkSupport.GradleJavaFrameworkSupportProvider" />
|
||||
<gradleFrameworkSupport implementation="org.jetbrains.kotlin.gradle.kdsl.frameworkSupport.GradleKotlinDSLKotlinJavaFrameworkSupportProvider" />
|
||||
<gradleFrameworkSupport implementation="org.jetbrains.kotlin.gradle.kdsl.frameworkSupport.GradleKotlinDSLKotlinJSFrameworkSupportProvider" />
|
||||
<gradleFrameworkSupport implementation="org.jetbrains.kotlin.gradle.kdsl.frameworkSupport.GradleGroovyFrameworkSupportProvider" />
|
||||
<buildSystemTypeDetector implementation="org.jetbrains.kotlin.idea.configuration.GradleDetector"/>
|
||||
</extensions>
|
||||
</idea-plugin>
|
||||
File diff suppressed because it is too large
Load Diff
-178
@@ -1,178 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.highlighter
|
||||
|
||||
import com.intellij.codeHighlighting.Pass
|
||||
import com.intellij.codeInsight.daemon.LineMarkerInfo
|
||||
import com.intellij.codeInsight.daemon.LineMarkerProvider
|
||||
import com.intellij.icons.AllIcons
|
||||
import com.intellij.openapi.editor.markup.GutterIconRenderer
|
||||
import com.intellij.openapi.progress.ProgressManager
|
||||
import com.intellij.psi.PsiDocumentManager
|
||||
import com.intellij.psi.PsiElement
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.SimpleFunctionDescriptor
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
||||
import org.jetbrains.kotlin.idea.inspections.RecursivePropertyAccessorInspection
|
||||
import org.jetbrains.kotlin.idea.util.getReceiverTargetDescriptor
|
||||
import org.jetbrains.kotlin.lexer.KtToken
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.parents
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.inline.InlineUtil
|
||||
import org.jetbrains.kotlin.resolve.scopes.receivers.Receiver
|
||||
import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue
|
||||
import org.jetbrains.kotlin.types.expressions.OperatorConventions
|
||||
import org.jetbrains.kotlin.util.OperatorNameConventions
|
||||
import java.util.*
|
||||
|
||||
class KotlinRecursiveCallLineMarkerProvider : LineMarkerProvider {
|
||||
override fun getLineMarkerInfo(element: PsiElement) = null
|
||||
|
||||
override fun collectSlowLineMarkers(elements: MutableList<PsiElement>, result: MutableCollection<LineMarkerInfo<*>>) {
|
||||
val markedLineNumbers = HashSet<Int>()
|
||||
|
||||
for (element in elements) {
|
||||
ProgressManager.checkCanceled()
|
||||
if (element is KtElement) {
|
||||
val lineNumber = element.getLineNumber()
|
||||
if (lineNumber !in markedLineNumbers && isRecursiveCall(element)) {
|
||||
markedLineNumbers.add(lineNumber)
|
||||
result.add(RecursiveMethodCallMarkerInfo(element))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getEnclosingFunction(element: KtElement, stopOnNonInlinedLambdas: Boolean): KtNamedFunction? {
|
||||
for (parent in element.parents) {
|
||||
when (parent) {
|
||||
is KtFunctionLiteral -> if (stopOnNonInlinedLambdas && !InlineUtil.isInlinedArgument(
|
||||
parent,
|
||||
parent.analyze(),
|
||||
false
|
||||
)
|
||||
) return null
|
||||
is KtNamedFunction -> {
|
||||
when (parent.parent) {
|
||||
is KtBlockExpression, is KtClassBody, is KtFile, is KtScript -> return parent
|
||||
else -> if (stopOnNonInlinedLambdas && !InlineUtil.isInlinedArgument(parent, parent.analyze(), false)) return null
|
||||
}
|
||||
}
|
||||
is KtClassOrObject -> return null
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
private fun isRecursiveCall(element: KtElement): Boolean {
|
||||
if (RecursivePropertyAccessorInspection.isRecursivePropertyAccess(element)) return true
|
||||
if (RecursivePropertyAccessorInspection.isRecursiveSyntheticPropertyAccess(element)) return true
|
||||
// Fast check for names without resolve
|
||||
val resolveName = getCallNameFromPsi(element) ?: return false
|
||||
val enclosingFunction = getEnclosingFunction(element, false) ?: return false
|
||||
|
||||
val enclosingFunctionName = enclosingFunction.name
|
||||
if (enclosingFunctionName != OperatorNameConventions.INVOKE.asString()
|
||||
&& enclosingFunctionName != resolveName.asString()
|
||||
) return false
|
||||
|
||||
// Check that there were no not-inlined lambdas on the way to enclosing function
|
||||
if (enclosingFunction != getEnclosingFunction(element, true)) return false
|
||||
|
||||
val bindingContext = element.analyze()
|
||||
val enclosingFunctionDescriptor = bindingContext[BindingContext.FUNCTION, enclosingFunction] ?: return false
|
||||
|
||||
val call = bindingContext[BindingContext.CALL, element] ?: return false
|
||||
val resolvedCall = bindingContext[BindingContext.RESOLVED_CALL, call] ?: return false
|
||||
|
||||
if (resolvedCall.candidateDescriptor.original != enclosingFunctionDescriptor) return false
|
||||
|
||||
fun isDifferentReceiver(receiver: Receiver?): Boolean {
|
||||
if (receiver !is ReceiverValue) return false
|
||||
|
||||
val receiverOwner = receiver.getReceiverTargetDescriptor(bindingContext) ?: return true
|
||||
|
||||
return when (receiverOwner) {
|
||||
is SimpleFunctionDescriptor -> receiverOwner != enclosingFunctionDescriptor
|
||||
is ClassDescriptor -> receiverOwner != enclosingFunctionDescriptor.containingDeclaration
|
||||
else -> return true
|
||||
}
|
||||
}
|
||||
|
||||
if (isDifferentReceiver(resolvedCall.dispatchReceiver)) return false
|
||||
return true
|
||||
}
|
||||
|
||||
private class RecursiveMethodCallMarkerInfo(callElement: KtElement) : LineMarkerInfo<KtElement>(
|
||||
callElement,
|
||||
callElement.textRange,
|
||||
AllIcons.Gutter.RecursiveMethod,
|
||||
Pass.LINE_MARKERS,
|
||||
{ "Recursive call" },
|
||||
null,
|
||||
GutterIconRenderer.Alignment.RIGHT
|
||||
) {
|
||||
|
||||
override fun createGutterRenderer(): GutterIconRenderer? {
|
||||
return object : LineMarkerInfo.LineMarkerGutterIconRenderer<KtElement>(this) {
|
||||
override fun getClickAction() = null // to place breakpoint on mouse click
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private fun PsiElement.getLineNumber(): Int {
|
||||
return PsiDocumentManager.getInstance(project).getDocument(containingFile)!!.getLineNumber(textOffset)
|
||||
}
|
||||
|
||||
private fun getCallNameFromPsi(element: KtElement): Name? {
|
||||
when (element) {
|
||||
is KtSimpleNameExpression -> {
|
||||
val elementParent = element.getParent()
|
||||
when (elementParent) {
|
||||
is KtCallExpression -> return Name.identifier(element.getText())
|
||||
is KtOperationExpression -> {
|
||||
val operationReference = elementParent.operationReference
|
||||
if (element == operationReference) {
|
||||
val node = operationReference.getReferencedNameElementType()
|
||||
return if (node is KtToken) {
|
||||
val conventionName = if (elementParent is KtPrefixExpression)
|
||||
OperatorConventions.getNameForOperationSymbol(node, true, false)
|
||||
else
|
||||
OperatorConventions.getNameForOperationSymbol(node)
|
||||
|
||||
conventionName ?: Name.identifier(element.getText())
|
||||
} else {
|
||||
Name.identifier(element.getText())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
is KtArrayAccessExpression ->
|
||||
return OperatorNameConventions.GET
|
||||
is KtThisExpression ->
|
||||
if (element.getParent() is KtCallExpression) {
|
||||
return OperatorNameConventions.INVOKE
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
-120
@@ -1,120 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2018 JetBrains s.r.o. 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.highlighter
|
||||
|
||||
import com.intellij.codeHighlighting.Pass
|
||||
import com.intellij.codeInsight.daemon.LineMarkerInfo
|
||||
import com.intellij.codeInsight.daemon.LineMarkerProvider
|
||||
import com.intellij.openapi.actionSystem.AnAction
|
||||
import com.intellij.openapi.editor.markup.GutterIconRenderer
|
||||
import com.intellij.openapi.progress.ProgressManager
|
||||
import com.intellij.psi.PsiElement
|
||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.VariableDescriptorWithAccessors
|
||||
import org.jetbrains.kotlin.descriptors.accessors
|
||||
import org.jetbrains.kotlin.idea.KotlinIcons
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
||||
import org.jetbrains.kotlin.idea.refactoring.getLineNumber
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.BindingContext.*
|
||||
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
|
||||
import org.jetbrains.kotlin.resolve.calls.checkers.isBuiltInCoroutineContext
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
|
||||
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
|
||||
|
||||
class KotlinSuspendCallLineMarkerProvider : LineMarkerProvider {
|
||||
private class SuspendCallMarkerInfo(callElement: KtElement, message: String) : LineMarkerInfo<KtElement>(
|
||||
callElement,
|
||||
callElement.textRange,
|
||||
KotlinIcons.SUSPEND_CALL,
|
||||
Pass.LINE_MARKERS,
|
||||
{ message },
|
||||
null,
|
||||
GutterIconRenderer.Alignment.RIGHT
|
||||
) {
|
||||
override fun createGutterRenderer(): GutterIconRenderer? {
|
||||
return object : LineMarkerInfo.LineMarkerGutterIconRenderer<KtElement>(this) {
|
||||
override fun getClickAction(): AnAction? = null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun getLineMarkerInfo(element: PsiElement): LineMarkerInfo<*>? = null
|
||||
|
||||
override fun collectSlowLineMarkers(
|
||||
elements: MutableList<PsiElement>,
|
||||
result: MutableCollection<LineMarkerInfo<*>>
|
||||
) {
|
||||
val markedLineNumbers = HashSet<Int>()
|
||||
|
||||
for (element in elements) {
|
||||
ProgressManager.checkCanceled()
|
||||
|
||||
if (element !is KtExpression) continue
|
||||
|
||||
val containingFile = element.containingFile
|
||||
if (containingFile !is KtFile || containingFile is KtCodeFragment) {
|
||||
continue
|
||||
}
|
||||
|
||||
val lineNumber = element.getLineNumber()
|
||||
if (lineNumber in markedLineNumbers) continue
|
||||
if (!element.hasSuspendCalls()) continue
|
||||
|
||||
markedLineNumbers += lineNumber
|
||||
result += if (element is KtForExpression) {
|
||||
SuspendCallMarkerInfo(element.loopRange!!, "Suspending iteration")
|
||||
} else {
|
||||
SuspendCallMarkerInfo(element, "Suspend function call")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun KtExpression.isValidCandidateExpression(): Boolean {
|
||||
if (this is KtOperationReferenceExpression || this is KtForExpression || this is KtProperty || this is KtNameReferenceExpression) return true
|
||||
val parent = parent
|
||||
if (parent is KtCallExpression && parent.calleeExpression == this) return true
|
||||
return false
|
||||
}
|
||||
|
||||
fun KtExpression.hasSuspendCalls(bindingContext: BindingContext = analyze(BodyResolveMode.PARTIAL)): Boolean {
|
||||
if (!isValidCandidateExpression()) return false
|
||||
|
||||
return when (this) {
|
||||
is KtForExpression -> {
|
||||
val iteratorResolvedCall = bindingContext[LOOP_RANGE_ITERATOR_RESOLVED_CALL, loopRange]
|
||||
val loopRangeHasNextResolvedCall = bindingContext[LOOP_RANGE_HAS_NEXT_RESOLVED_CALL, loopRange]
|
||||
val loopRangeNextResolvedCall = bindingContext[LOOP_RANGE_NEXT_RESOLVED_CALL, loopRange]
|
||||
listOf(iteratorResolvedCall, loopRangeHasNextResolvedCall, loopRangeNextResolvedCall).any {
|
||||
it?.resultingDescriptor?.isSuspend == true
|
||||
}
|
||||
}
|
||||
is KtProperty -> {
|
||||
if (hasDelegateExpression()) {
|
||||
val variableDescriptor = bindingContext[DECLARATION_TO_DESCRIPTOR, this] as? VariableDescriptorWithAccessors
|
||||
val accessors = variableDescriptor?.accessors ?: emptyList()
|
||||
accessors.any { accessor ->
|
||||
val delegatedFunctionDescriptor = bindingContext[DELEGATED_PROPERTY_RESOLVED_CALL, accessor]?.resultingDescriptor
|
||||
delegatedFunctionDescriptor?.isSuspend == true
|
||||
}
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
val resolvedCall = getResolvedCall(bindingContext)
|
||||
if ((resolvedCall?.resultingDescriptor as? FunctionDescriptor)?.isSuspend == true) true
|
||||
else {
|
||||
val propertyDescriptor = resolvedCall?.resultingDescriptor as? PropertyDescriptor
|
||||
val s = propertyDescriptor?.fqNameSafe?.asString()
|
||||
s?.startsWith("kotlin.coroutines.") == true && s.endsWith(".coroutineContext")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
-434
@@ -1,434 +0,0 @@
|
||||
/*
|
||||
* Copyright 2000-2018 JetBrains s.r.o. 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.highlighter.markers
|
||||
|
||||
import com.intellij.codeHighlighting.Pass
|
||||
import com.intellij.codeInsight.daemon.*
|
||||
import com.intellij.codeInsight.daemon.impl.LineMarkerNavigator
|
||||
import com.intellij.codeInsight.daemon.impl.MarkerType
|
||||
import com.intellij.codeInsight.daemon.impl.PsiElementListNavigator
|
||||
import com.intellij.codeInsight.navigation.ListBackgroundUpdaterTask
|
||||
import com.intellij.icons.AllIcons
|
||||
import com.intellij.openapi.actionSystem.IdeActions
|
||||
import com.intellij.openapi.editor.colors.CodeInsightColors
|
||||
import com.intellij.openapi.editor.colors.EditorColorsManager
|
||||
import com.intellij.openapi.editor.markup.GutterIconRenderer
|
||||
import com.intellij.openapi.editor.markup.SeparatorPlacement
|
||||
import com.intellij.openapi.progress.ProgressManager
|
||||
import com.intellij.openapi.project.DumbService
|
||||
import com.intellij.openapi.util.text.StringUtil
|
||||
import com.intellij.psi.*
|
||||
import com.intellij.psi.search.searches.ClassInheritorsSearch
|
||||
import com.intellij.psi.util.PsiTreeUtil
|
||||
import org.jetbrains.kotlin.asJava.LightClassUtil
|
||||
import org.jetbrains.kotlin.asJava.toLightClass
|
||||
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.MemberDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.Modality
|
||||
import org.jetbrains.kotlin.idea.KotlinIcons
|
||||
import org.jetbrains.kotlin.idea.caches.lightClasses.KtFakeLightClass
|
||||
import org.jetbrains.kotlin.idea.caches.lightClasses.KtFakeLightMethod
|
||||
import org.jetbrains.kotlin.idea.caches.project.implementedDescriptors
|
||||
import org.jetbrains.kotlin.idea.caches.project.implementingDescriptors
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.findModuleDescriptor
|
||||
import org.jetbrains.kotlin.idea.core.isInheritable
|
||||
import org.jetbrains.kotlin.idea.core.isOverridable
|
||||
import org.jetbrains.kotlin.idea.core.toDescriptor
|
||||
import org.jetbrains.kotlin.idea.presentation.DeclarationByModuleRenderer
|
||||
import org.jetbrains.kotlin.idea.search.declarationsSearch.toPossiblyFakeLightMethods
|
||||
import org.jetbrains.kotlin.idea.util.*
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.getPrevSiblingIgnoringWhitespaceAndComments
|
||||
import java.awt.event.MouseEvent
|
||||
import java.util.*
|
||||
import javax.swing.Icon
|
||||
import javax.swing.ListCellRenderer
|
||||
|
||||
class KotlinLineMarkerProvider : LineMarkerProvider {
|
||||
override fun getLineMarkerInfo(element: PsiElement): LineMarkerInfo<PsiElement>? {
|
||||
if (DaemonCodeAnalyzerSettings.getInstance().SHOW_METHOD_SEPARATORS) {
|
||||
if (element.canHaveSeparator()) {
|
||||
val prevSibling = element.getPrevSiblingIgnoringWhitespaceAndComments()
|
||||
if (prevSibling.canHaveSeparator() &&
|
||||
(element.wantsSeparator() || prevSibling?.wantsSeparator() == true)
|
||||
) {
|
||||
return createLineSeparatorByElement(element)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
private fun PsiElement?.canHaveSeparator() =
|
||||
this is KtFunction || this is KtClassInitializer || (this is KtProperty && !isLocal)
|
||||
|
||||
private fun PsiElement.wantsSeparator() = StringUtil.getLineBreakCount(text) > 0
|
||||
|
||||
private fun createLineSeparatorByElement(element: PsiElement): LineMarkerInfo<PsiElement> {
|
||||
val anchor = PsiTreeUtil.getDeepestFirst(element)
|
||||
|
||||
val info = LineMarkerInfo(anchor, anchor.textRange, null, Pass.LINE_MARKERS, null, null, GutterIconRenderer.Alignment.RIGHT)
|
||||
info.separatorColor = EditorColorsManager.getInstance().globalScheme.getColor(CodeInsightColors.METHOD_SEPARATORS_COLOR)
|
||||
info.separatorPlacement = SeparatorPlacement.TOP
|
||||
return info
|
||||
}
|
||||
|
||||
override fun collectSlowLineMarkers(elements: List<PsiElement>, result: MutableCollection<LineMarkerInfo<*>>) {
|
||||
if (elements.isEmpty()) return
|
||||
|
||||
val first = elements.first()
|
||||
if (DumbService.getInstance(first.project).isDumb || !ProjectRootsUtil.isInProjectOrLibSource(first)) return
|
||||
|
||||
val functions = hashSetOf<KtNamedFunction>()
|
||||
val properties = hashSetOf<KtNamedDeclaration>()
|
||||
val declarations = hashSetOf<KtNamedDeclaration>()
|
||||
|
||||
for (leaf in elements) {
|
||||
ProgressManager.checkCanceled()
|
||||
val element = leaf.parent as? KtNamedDeclaration ?: continue
|
||||
if (!declarations.add(element)) continue
|
||||
|
||||
when (element) {
|
||||
is KtClass -> {
|
||||
collectInheritedClassMarker(element, result)
|
||||
collectHighlightingColorsMarkers(element, result)
|
||||
}
|
||||
is KtNamedFunction -> {
|
||||
functions.add(element)
|
||||
collectSuperDeclarationMarkers(element, result)
|
||||
}
|
||||
is KtProperty -> {
|
||||
properties.add(element)
|
||||
collectSuperDeclarationMarkers(element, result)
|
||||
}
|
||||
is KtParameter -> {
|
||||
if (element.hasValOrVar()) {
|
||||
properties.add(element)
|
||||
collectSuperDeclarationMarkers(element, result)
|
||||
}
|
||||
}
|
||||
}
|
||||
collectMultiplatformMarkers(element, result)
|
||||
}
|
||||
|
||||
collectOverriddenFunctions(functions, result)
|
||||
collectOverriddenPropertyAccessors(properties, result)
|
||||
}
|
||||
}
|
||||
|
||||
private val OVERRIDING_MARK: Icon = AllIcons.Gutter.OverridingMethod
|
||||
private val IMPLEMENTING_MARK: Icon = AllIcons.Gutter.ImplementingMethod
|
||||
private val OVERRIDDEN_MARK: Icon = AllIcons.Gutter.OverridenMethod
|
||||
private val IMPLEMENTED_MARK: Icon = AllIcons.Gutter.ImplementedMethod
|
||||
|
||||
data class NavigationPopupDescriptor(
|
||||
val targets: Collection<NavigatablePsiElement>,
|
||||
val title: String,
|
||||
val findUsagesTitle: String,
|
||||
val renderer: ListCellRenderer<*>,
|
||||
val updater: ListBackgroundUpdaterTask? = null
|
||||
) {
|
||||
fun showPopup(e: MouseEvent?) {
|
||||
PsiElementListNavigator.openTargets(e, targets.toTypedArray(), title, findUsagesTitle, renderer, updater)
|
||||
}
|
||||
}
|
||||
|
||||
interface TestableLineMarkerNavigator {
|
||||
fun getTargetsPopupDescriptor(element: PsiElement?): NavigationPopupDescriptor?
|
||||
}
|
||||
|
||||
private val SUBCLASSED_CLASS = MarkerType(
|
||||
"SUBCLASSED_CLASS",
|
||||
{ getPsiClass(it)?.let(::getSubclassedClassTooltip) },
|
||||
object : LineMarkerNavigator() {
|
||||
override fun browse(e: MouseEvent?, element: PsiElement?) {
|
||||
getPsiClass(element)?.let { MarkerType.navigateToSubclassedClass(e, it, DeclarationByModuleRenderer()) }
|
||||
}
|
||||
})
|
||||
|
||||
private val OVERRIDDEN_FUNCTION = object : MarkerType(
|
||||
"OVERRIDDEN_FUNCTION",
|
||||
{ getPsiMethod(it)?.let(::getOverriddenMethodTooltip) },
|
||||
object : LineMarkerNavigator() {
|
||||
override fun browse(e: MouseEvent?, element: PsiElement?) {
|
||||
buildNavigateToOverriddenMethodPopup(e, element)?.showPopup(e)
|
||||
}
|
||||
}) {
|
||||
|
||||
override fun getNavigationHandler(): GutterIconNavigationHandler<PsiElement> {
|
||||
val superHandler = super.getNavigationHandler()
|
||||
return object : GutterIconNavigationHandler<PsiElement>, TestableLineMarkerNavigator {
|
||||
override fun navigate(e: MouseEvent?, elt: PsiElement?) {
|
||||
superHandler.navigate(e, elt)
|
||||
}
|
||||
|
||||
override fun getTargetsPopupDescriptor(element: PsiElement?) = buildNavigateToOverriddenMethodPopup(null, element)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val OVERRIDDEN_PROPERTY = object : MarkerType(
|
||||
"OVERRIDDEN_PROPERTY",
|
||||
{ it?.let { getOverriddenPropertyTooltip(it.parent as KtNamedDeclaration) } },
|
||||
object : LineMarkerNavigator() {
|
||||
override fun browse(e: MouseEvent?, element: PsiElement?) {
|
||||
buildNavigateToPropertyOverriddenDeclarationsPopup(e, element)?.showPopup(e)
|
||||
}
|
||||
}) {
|
||||
|
||||
override fun getNavigationHandler(): GutterIconNavigationHandler<PsiElement> {
|
||||
val superHandler = super.getNavigationHandler()
|
||||
return object : GutterIconNavigationHandler<PsiElement>, TestableLineMarkerNavigator {
|
||||
override fun navigate(e: MouseEvent?, elt: PsiElement?) {
|
||||
superHandler.navigate(e, elt)
|
||||
}
|
||||
|
||||
override fun getTargetsPopupDescriptor(element: PsiElement?) = buildNavigateToPropertyOverriddenDeclarationsPopup(null, element)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val PsiElement.markerDeclaration
|
||||
get() = (this as? KtDeclaration) ?: (parent as? KtDeclaration)
|
||||
|
||||
private val PLATFORM_ACTUAL = MarkerType(
|
||||
"PLATFORM_ACTUAL",
|
||||
{ it?.let { getPlatformActualTooltip(it.markerDeclaration) } },
|
||||
object : LineMarkerNavigator() {
|
||||
override fun browse(e: MouseEvent?, element: PsiElement?) {
|
||||
element?.let { navigateToPlatformActual(e, it.markerDeclaration) }
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
private val EXPECTED_DECLARATION = MarkerType(
|
||||
"EXPECTED_DECLARATION",
|
||||
{ it?.let { getExpectedDeclarationTooltip(it.markerDeclaration) } },
|
||||
object : LineMarkerNavigator() {
|
||||
override fun browse(e: MouseEvent?, element: PsiElement?) {
|
||||
element?.let { navigateToExpectedDeclaration(it.markerDeclaration) }
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
private fun isImplementsAndNotOverrides(
|
||||
descriptor: CallableMemberDescriptor,
|
||||
overriddenMembers: Collection<CallableMemberDescriptor>
|
||||
): Boolean {
|
||||
return descriptor.modality != Modality.ABSTRACT && overriddenMembers.all { it.modality == Modality.ABSTRACT }
|
||||
}
|
||||
|
||||
private fun collectSuperDeclarationMarkers(declaration: KtDeclaration, result: MutableCollection<LineMarkerInfo<*>>) {
|
||||
assert(declaration is KtNamedFunction || declaration is KtProperty || declaration is KtParameter)
|
||||
|
||||
if (!declaration.hasModifier(KtTokens.OVERRIDE_KEYWORD)) return
|
||||
|
||||
val resolveWithParents = resolveDeclarationWithParents(declaration)
|
||||
if (resolveWithParents.overriddenDescriptors.isEmpty()) return
|
||||
|
||||
val implements = isImplementsAndNotOverrides(resolveWithParents.descriptor!!, resolveWithParents.overriddenDescriptors)
|
||||
|
||||
val anchor = (declaration as? KtNamedDeclaration)?.nameIdentifier ?: declaration
|
||||
|
||||
// NOTE: Don't store descriptors in line markers because line markers are not deleted while editing other files and this can prevent
|
||||
// clearing the whole BindingTrace.
|
||||
|
||||
val lineMarkerInfo = LineMarkerInfo(
|
||||
anchor,
|
||||
anchor.textRange,
|
||||
if (implements) IMPLEMENTING_MARK else OVERRIDING_MARK,
|
||||
Pass.LINE_MARKERS,
|
||||
SuperDeclarationMarkerTooltip,
|
||||
SuperDeclarationMarkerNavigationHandler(),
|
||||
GutterIconRenderer.Alignment.RIGHT
|
||||
)
|
||||
NavigateAction.setNavigateAction(
|
||||
lineMarkerInfo,
|
||||
if (declaration is KtNamedFunction) "Go to super method" else "Go to super property",
|
||||
IdeActions.ACTION_GOTO_SUPER
|
||||
)
|
||||
result.add(lineMarkerInfo)
|
||||
}
|
||||
|
||||
private fun collectInheritedClassMarker(element: KtClass, result: MutableCollection<LineMarkerInfo<*>>) {
|
||||
if (!element.isInheritable()) {
|
||||
return
|
||||
}
|
||||
|
||||
val lightClass = element.toLightClass() ?: KtFakeLightClass(element)
|
||||
|
||||
if (ClassInheritorsSearch.search(lightClass, false).findFirst() == null) return
|
||||
|
||||
val anchor = element.nameIdentifier ?: element
|
||||
|
||||
val lineMarkerInfo = LineMarkerInfo(
|
||||
anchor,
|
||||
anchor.textRange,
|
||||
if (element.isInterface()) IMPLEMENTED_MARK else OVERRIDDEN_MARK,
|
||||
Pass.LINE_MARKERS,
|
||||
SUBCLASSED_CLASS.tooltip,
|
||||
SUBCLASSED_CLASS.navigationHandler,
|
||||
GutterIconRenderer.Alignment.RIGHT
|
||||
)
|
||||
NavigateAction.setNavigateAction(
|
||||
lineMarkerInfo,
|
||||
if (element.isInterface()) "Go to implementations" else "Go to subclasses",
|
||||
IdeActions.ACTION_GOTO_IMPLEMENTATION
|
||||
)
|
||||
result.add(lineMarkerInfo)
|
||||
}
|
||||
|
||||
private fun collectOverriddenPropertyAccessors(
|
||||
properties: Collection<KtNamedDeclaration>,
|
||||
result: MutableCollection<LineMarkerInfo<*>>
|
||||
) {
|
||||
val mappingToJava = HashMap<PsiElement, KtNamedDeclaration>()
|
||||
for (property in properties) {
|
||||
if (property.isOverridable()) {
|
||||
property.toPossiblyFakeLightMethods().forEach { mappingToJava[it] = property }
|
||||
mappingToJava[property] = property
|
||||
}
|
||||
}
|
||||
|
||||
val classes = collectContainingClasses(mappingToJava.keys.filterIsInstance<PsiMethod>())
|
||||
|
||||
for (property in getOverriddenDeclarations(mappingToJava, classes)) {
|
||||
ProgressManager.checkCanceled()
|
||||
|
||||
val anchor = (property as? PsiNameIdentifierOwner)?.nameIdentifier ?: property
|
||||
|
||||
val lineMarkerInfo = LineMarkerInfo(
|
||||
anchor,
|
||||
anchor.textRange,
|
||||
if (isImplemented(property)) IMPLEMENTED_MARK else OVERRIDDEN_MARK,
|
||||
Pass.LINE_MARKERS,
|
||||
OVERRIDDEN_PROPERTY.tooltip,
|
||||
OVERRIDDEN_PROPERTY.navigationHandler,
|
||||
GutterIconRenderer.Alignment.RIGHT
|
||||
)
|
||||
NavigateAction.setNavigateAction(
|
||||
lineMarkerInfo,
|
||||
"Go to overridden properties",
|
||||
IdeActions.ACTION_GOTO_IMPLEMENTATION
|
||||
)
|
||||
result.add(lineMarkerInfo)
|
||||
}
|
||||
}
|
||||
|
||||
private val KtNamedDeclaration.expectOrActualAnchor
|
||||
get() =
|
||||
nameIdentifier ?: when (this) {
|
||||
is KtConstructor<*> -> getConstructorKeyword() ?: getValueParameterList()?.leftParenthesis
|
||||
is KtObjectDeclaration -> getObjectKeyword()
|
||||
else -> null
|
||||
} ?: this
|
||||
|
||||
private fun collectMultiplatformMarkers(
|
||||
declaration: KtNamedDeclaration,
|
||||
result: MutableCollection<LineMarkerInfo<*>>
|
||||
) {
|
||||
when {
|
||||
declaration.isExpectDeclaration() -> collectActualMarkers(declaration, result)
|
||||
declaration.isEffectivelyActual() -> collectExpectedMarkers(declaration, result)
|
||||
}
|
||||
}
|
||||
|
||||
private fun collectActualMarkers(
|
||||
declaration: KtNamedDeclaration,
|
||||
result: MutableCollection<LineMarkerInfo<*>>
|
||||
) {
|
||||
if (declaration is KtPrimaryConstructor) return
|
||||
|
||||
val descriptor = declaration.toDescriptor() as? MemberDescriptor ?: return
|
||||
val commonModuleDescriptor = declaration.containingKtFile.findModuleDescriptor()
|
||||
|
||||
if (commonModuleDescriptor.implementingDescriptors.none { it.hasActualsFor(descriptor) }) return
|
||||
|
||||
val anchor = declaration.expectOrActualAnchor
|
||||
|
||||
val lineMarkerInfo = LineMarkerInfo(
|
||||
anchor,
|
||||
anchor.textRange,
|
||||
KotlinIcons.ACTUAL,
|
||||
Pass.LINE_MARKERS,
|
||||
PLATFORM_ACTUAL.tooltip,
|
||||
PLATFORM_ACTUAL.navigationHandler,
|
||||
GutterIconRenderer.Alignment.RIGHT
|
||||
)
|
||||
NavigateAction.setNavigateAction(
|
||||
lineMarkerInfo,
|
||||
"Go to actual declarations",
|
||||
IdeActions.ACTION_GOTO_IMPLEMENTATION
|
||||
)
|
||||
result.add(lineMarkerInfo)
|
||||
}
|
||||
|
||||
private fun collectExpectedMarkers(
|
||||
declaration: KtNamedDeclaration,
|
||||
result: MutableCollection<LineMarkerInfo<*>>
|
||||
) {
|
||||
if (declaration is KtPrimaryConstructor) return
|
||||
|
||||
val descriptor = declaration.toDescriptor() as? MemberDescriptor ?: return
|
||||
val platformModuleDescriptor = declaration.containingKtFile.findModuleDescriptor()
|
||||
if (!platformModuleDescriptor.implementedDescriptors.any { it.hasDeclarationOf(descriptor) }) return
|
||||
|
||||
val anchor = declaration.expectOrActualAnchor
|
||||
|
||||
val lineMarkerInfo = LineMarkerInfo(
|
||||
anchor,
|
||||
anchor.textRange,
|
||||
KotlinIcons.EXPECT,
|
||||
Pass.LINE_MARKERS,
|
||||
EXPECTED_DECLARATION.tooltip,
|
||||
EXPECTED_DECLARATION.navigationHandler,
|
||||
GutterIconRenderer.Alignment.RIGHT
|
||||
)
|
||||
NavigateAction.setNavigateAction(
|
||||
lineMarkerInfo,
|
||||
"Go to expected declaration",
|
||||
null
|
||||
)
|
||||
result.add(lineMarkerInfo)
|
||||
}
|
||||
|
||||
private fun collectOverriddenFunctions(functions: Collection<KtNamedFunction>, result: MutableCollection<LineMarkerInfo<*>>) {
|
||||
val mappingToJava = HashMap<PsiElement, KtNamedFunction>()
|
||||
for (function in functions) {
|
||||
if (function.isOverridable()) {
|
||||
val method = LightClassUtil.getLightClassMethod(function) ?: KtFakeLightMethod.get(function)
|
||||
if (method != null) {
|
||||
mappingToJava[method] = function
|
||||
}
|
||||
mappingToJava[function] = function
|
||||
}
|
||||
}
|
||||
|
||||
val classes = collectContainingClasses(mappingToJava.keys.filterIsInstance<PsiMethod>())
|
||||
|
||||
for (function in getOverriddenDeclarations(mappingToJava, classes)) {
|
||||
ProgressManager.checkCanceled()
|
||||
|
||||
val anchor = function.nameIdentifier ?: function
|
||||
|
||||
val lineMarkerInfo = LineMarkerInfo(
|
||||
anchor,
|
||||
anchor.textRange,
|
||||
if (isImplemented(function)) IMPLEMENTED_MARK else OVERRIDDEN_MARK,
|
||||
Pass.LINE_MARKERS, OVERRIDDEN_FUNCTION.tooltip,
|
||||
OVERRIDDEN_FUNCTION.navigationHandler,
|
||||
GutterIconRenderer.Alignment.RIGHT
|
||||
)
|
||||
NavigateAction.setNavigateAction(
|
||||
lineMarkerInfo,
|
||||
"Go to overridden methods",
|
||||
IdeActions.ACTION_GOTO_IMPLEMENTATION
|
||||
)
|
||||
result.add(lineMarkerInfo)
|
||||
}
|
||||
}
|
||||
-261
@@ -1,261 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.intentions
|
||||
|
||||
import com.intellij.codeInsight.intention.IntentionAction
|
||||
import com.intellij.codeInsight.intention.JvmCommonIntentionActionsFactory
|
||||
import com.intellij.codeInsight.intention.MethodInsertionInfo
|
||||
import com.intellij.codeInsight.intention.QuickFixFactory
|
||||
import com.intellij.codeInspection.LocalQuickFixAndIntentionActionOnPsiElement
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.psi.*
|
||||
import com.intellij.psi.impl.source.tree.java.PsiReferenceExpressionImpl
|
||||
import org.jetbrains.kotlin.asJava.LightClassUtil
|
||||
import org.jetbrains.kotlin.asJava.elements.KtLightElement
|
||||
import org.jetbrains.kotlin.builtins.DefaultBuiltIns
|
||||
import org.jetbrains.kotlin.builtins.jvm.JavaToKotlinClassMap
|
||||
import org.jetbrains.kotlin.descriptors.Visibilities
|
||||
import org.jetbrains.kotlin.idea.core.ShortenReferences
|
||||
import org.jetbrains.kotlin.idea.core.insertMembersAfter
|
||||
import org.jetbrains.kotlin.idea.quickfix.AddModifierFix
|
||||
import org.jetbrains.kotlin.idea.quickfix.RemoveModifierFix
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.KtPsiFactory.CallableBuilder
|
||||
import org.jetbrains.kotlin.psi.KtPsiFactory.CallableBuilder.Target.CONSTRUCTOR
|
||||
import org.jetbrains.kotlin.psi.KtPsiFactory.CallableBuilder.Target.FUNCTION
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
|
||||
import org.jetbrains.uast.UClass
|
||||
import org.jetbrains.uast.UDeclaration
|
||||
import org.jetbrains.uast.UElement
|
||||
|
||||
class KotlinCommonIntentionActionsFactory : JvmCommonIntentionActionsFactory() {
|
||||
override fun createChangeModifierAction(declaration: UDeclaration, modifier: String, shouldPresent: Boolean): IntentionAction? {
|
||||
val kModifierOwner = declaration.asKtElement<KtModifierListOwner>()
|
||||
?: throw IllegalArgumentException("$declaration is expected to contain KtLightElement with KtModifierListOwner")
|
||||
|
||||
val (kToken, shouldPresentMapped) = if (PsiModifier.FINAL == modifier)
|
||||
KtTokens.OPEN_KEYWORD to !shouldPresent
|
||||
else
|
||||
javaPsiModifiersMapping[modifier] to shouldPresent
|
||||
|
||||
if (kToken == null) return null
|
||||
return if (shouldPresentMapped)
|
||||
AddModifierFix.createIfApplicable(kModifierOwner, kToken)
|
||||
else
|
||||
RemoveModifierFix(kModifierOwner, kToken, false)
|
||||
}
|
||||
|
||||
override fun createAddBeanPropertyActions(uClass: UClass,
|
||||
propertyName: String,
|
||||
visibilityModifier: String,
|
||||
propertyType: PsiType,
|
||||
setterRequired: Boolean,
|
||||
getterRequired: Boolean): List<IntentionAction> {
|
||||
|
||||
fun addPropertyFix(lateinit: Boolean = false) =
|
||||
Fix(uClass,
|
||||
"Add property",
|
||||
"Add '${if (lateinit) "lateinit " else ""}" +
|
||||
"${if (setterRequired) "var" else "val"}' property '$propertyName' to '${uClass.name}'")
|
||||
{ uClass ->
|
||||
val visibilityStr = javaVisibilityMapping.getValue(visibilityModifier)
|
||||
val psiFactory = KtPsiFactory(uClass)
|
||||
val modifiersString = if (lateinit) "lateinit $visibilityStr" else visibilityStr
|
||||
val function = psiFactory.createProperty(
|
||||
modifiersString,
|
||||
propertyName,
|
||||
typeString(propertyType),
|
||||
setterRequired,
|
||||
if (lateinit) null else "TODO(\"initialize me\")")
|
||||
val ktClassOrObject = uClass.asKtElement<KtClassOrObject>()!!
|
||||
insertMembersAfter(null, ktClassOrObject, listOf(function), null)
|
||||
}
|
||||
|
||||
if (setterRequired)
|
||||
return listOf(addPropertyFix(), addPropertyFix(lateinit = true))
|
||||
else
|
||||
return listOf(addPropertyFix())
|
||||
}
|
||||
|
||||
override fun createAddCallableMemberActions(info: MethodInsertionInfo): List<IntentionAction> =
|
||||
when (info) {
|
||||
is MethodInsertionInfo.Method ->
|
||||
createAddMethodAction(info)?.let { listOf(it) } ?: emptyList()
|
||||
|
||||
is MethodInsertionInfo.Constructor ->
|
||||
createAddConstructorActions(info)
|
||||
}
|
||||
|
||||
companion object {
|
||||
val javaPsiModifiersMapping = mapOf(
|
||||
PsiModifier.PRIVATE to KtTokens.PRIVATE_KEYWORD,
|
||||
PsiModifier.PUBLIC to KtTokens.PUBLIC_KEYWORD,
|
||||
PsiModifier.PROTECTED to KtTokens.PUBLIC_KEYWORD,
|
||||
PsiModifier.ABSTRACT to KtTokens.ABSTRACT_KEYWORD
|
||||
)
|
||||
|
||||
val javaVisibilityMapping: Map<String, String> = mapOf(
|
||||
PsiModifier.PRIVATE to Visibilities.PRIVATE.displayName,
|
||||
PsiModifier.PUBLIC to "",
|
||||
PsiModifier.PROTECTED to Visibilities.PROTECTED.displayName,
|
||||
PsiModifier.PACKAGE_LOCAL to Visibilities.INTERNAL.displayName
|
||||
).withDefault { Visibilities.DEFAULT_VISIBILITY.displayName }
|
||||
|
||||
fun typeString(str: PsiType): String {
|
||||
var typeName: String? = when (str) {
|
||||
PsiType.VOID -> ""
|
||||
PsiType.INT -> "kotlin.Int"
|
||||
PsiType.LONG -> "kotlin.Long"
|
||||
PsiType.SHORT -> "kotlin.Short"
|
||||
PsiType.BOOLEAN -> "kotlin.Boolean"
|
||||
PsiType.BYTE -> "kotlin.Byte"
|
||||
PsiType.CHAR -> "kotlin.Char"
|
||||
PsiType.DOUBLE -> "kotlin.Double"
|
||||
PsiType.FLOAT -> "kotlin.Float"
|
||||
else -> null
|
||||
}
|
||||
if (typeName == null)
|
||||
typeName = JavaToKotlinClassMap.mapJavaToKotlin(FqName(str.canonicalText), DefaultBuiltIns.Instance)?.fqNameSafe?.asString()
|
||||
|
||||
return typeName ?: str.canonicalText
|
||||
}
|
||||
}
|
||||
|
||||
private inline fun <reified T : KtElement> UElement.asKtElement(): T? =
|
||||
(psi as? KtLightElement<*, *>?)?.kotlinOrigin as? T
|
||||
|
||||
private fun CallableBuilder.paramsFromInfo(info: MethodInsertionInfo) {
|
||||
for ((index, param) in info.parameters.withIndex()) {
|
||||
param(param.name ?: "arg${index + 1}", typeString(param.type))
|
||||
}
|
||||
}
|
||||
|
||||
private fun createAddMethodAction(info: MethodInsertionInfo.Method): IntentionAction? {
|
||||
val visibilityStr = info.modifiers.map { javaVisibilityMapping.get(it) ?: it }.joinToString(" ")
|
||||
val functionString = CallableBuilder(FUNCTION).apply {
|
||||
modifier(visibilityStr)
|
||||
typeParams()
|
||||
name(info.name)
|
||||
paramsFromInfo(info)
|
||||
info.returnType.let {
|
||||
when (it) {
|
||||
PsiType.VOID -> noReturnType()
|
||||
else -> returnType(typeString(it))
|
||||
}
|
||||
}
|
||||
blockBody("")
|
||||
}
|
||||
|
||||
return Fix(info.containingClass, "Add method", "Add method '${info.name}' to '${info.containingClass.name}'") {
|
||||
uClass ->
|
||||
val psiFactory = KtPsiFactory(uClass)
|
||||
val function = psiFactory.createFunction(functionString.asString())
|
||||
val ktClassOrObject = uClass.asKtElement<KtClassOrObject>()!!
|
||||
insertMembersAfter(null, ktClassOrObject, listOf(function), ktClassOrObject.declarations.lastOrNull())
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private fun createAddConstructorActions(info: MethodInsertionInfo.Constructor): List<IntentionAction> {
|
||||
val constructorString = CallableBuilder(CONSTRUCTOR).apply {
|
||||
modifier("")
|
||||
typeParams()
|
||||
name()
|
||||
paramsFromInfo(info)
|
||||
noReturnType()
|
||||
blockBody("")
|
||||
}.asString()
|
||||
val primaryConstructor = info.containingClass.asKtElement<KtClass>()!!.primaryConstructor
|
||||
|
||||
val addConstructorAction = if (primaryConstructor == null)
|
||||
Fix(info.containingClass,
|
||||
"Add method",
|
||||
"Add primary constructor to '${info.containingClass.name}'",
|
||||
{ uClass ->
|
||||
val psiFactory = KtPsiFactory(uClass)
|
||||
val constructor = psiFactory.createSecondaryConstructor(constructorString)
|
||||
val ktClass = uClass.asKtElement<KtClass>()!!
|
||||
val newPrimaryConstructor = ktClass.createPrimaryConstructorIfAbsent()
|
||||
newPrimaryConstructor.valueParameterList!!.replace(constructor.valueParameterList!!)
|
||||
ShortenReferences.DEFAULT.process(newPrimaryConstructor)
|
||||
})
|
||||
else Fix(info.containingClass,
|
||||
"Add method",
|
||||
"Add secondary constructor to '${info.containingClass.name}'",
|
||||
{ uClass ->
|
||||
val psiFactory = KtPsiFactory(uClass)
|
||||
val constructor = psiFactory.createSecondaryConstructor(constructorString)
|
||||
val ktClassOrObject = uClass.asKtElement<KtClassOrObject>()!!
|
||||
insertMembersAfter(null, ktClassOrObject, listOf(constructor), null)
|
||||
})
|
||||
|
||||
val changePrimaryConstructorAction = run {
|
||||
if (primaryConstructor == null) return@run null
|
||||
QuickFixFactory.getInstance()
|
||||
.createChangeMethodSignatureFromUsageFix(
|
||||
LightClassUtil.getLightClassMethod(primaryConstructor)!!,
|
||||
fakeParametersExpressions(info.parameters),
|
||||
PsiSubstitutor.EMPTY, info.containingClass, false, 2
|
||||
).takeIf { it.isAvailable(info.containingClass.project, null, info.containingClass.containingFile) }
|
||||
}
|
||||
|
||||
return listOf(changePrimaryConstructorAction, addConstructorAction).filterNotNull()
|
||||
}
|
||||
|
||||
private fun fakeParametersExpressions(parameters: List<PsiParameter>): Array<PsiExpression> =
|
||||
when {
|
||||
parameters.isEmpty() -> emptyArray<PsiExpression>()
|
||||
else -> JavaPsiFacade.getElementFactory(parameters.first().project)
|
||||
.createParameterList(
|
||||
parameters.map { it.name }.toTypedArray(),
|
||||
parameters.map { it.type }.toTypedArray()
|
||||
).parameters.map { FakeExpressionFromParameter(it) }.toTypedArray()
|
||||
}
|
||||
|
||||
private class FakeExpressionFromParameter(private val psiParam: PsiParameter) : PsiReferenceExpressionImpl() {
|
||||
|
||||
override fun getText(): String = psiParam.name!!
|
||||
|
||||
override fun getProject(): Project = psiParam.project
|
||||
|
||||
override fun getParent(): PsiElement = psiParam.parent
|
||||
|
||||
override fun getType(): PsiType? = psiParam.type
|
||||
|
||||
override fun isValid(): Boolean = true
|
||||
|
||||
override fun getContainingFile(): PsiFile = psiParam.containingFile
|
||||
|
||||
override fun getReferenceName(): String? = psiParam.name
|
||||
|
||||
override fun resolve(): PsiElement? = psiParam
|
||||
}
|
||||
|
||||
private class Fix(uClass: UClass, private val familyName: String, private val text: String, private val action: (uClass: UClass) -> Unit) : LocalQuickFixAndIntentionActionOnPsiElement(uClass) {
|
||||
override fun getFamilyName(): String = familyName
|
||||
|
||||
override fun getText(): String = text
|
||||
|
||||
override fun invoke(project: Project, file: PsiFile, editor: Editor?, startElement: PsiElement, endElement: PsiElement) =
|
||||
action(startElement as UClass)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,68 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.quickfix
|
||||
|
||||
import com.intellij.codeInsight.FileModificationService
|
||||
import com.intellij.codeInsight.intention.IntentionAction
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.PsiFile
|
||||
import org.jetbrains.kotlin.psi.CREATEBYPATTERN_MAY_NOT_REFORMAT
|
||||
import org.jetbrains.kotlin.psi.KtCodeFragment
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.psi.psiUtil.createSmartPointer
|
||||
|
||||
abstract class KotlinQuickFixAction<out T : PsiElement>(element: T) : IntentionAction {
|
||||
private val elementPointer = element.createSmartPointer()
|
||||
|
||||
protected val element: T?
|
||||
get() = elementPointer.element
|
||||
|
||||
final override fun isAvailable(project: Project, editor: Editor?, file: PsiFile): Boolean {
|
||||
if (ApplicationManager.getApplication().isUnitTestMode) {
|
||||
CREATEBYPATTERN_MAY_NOT_REFORMAT = true
|
||||
}
|
||||
try {
|
||||
val element = element ?: return false
|
||||
return element.isValid &&
|
||||
!element.project.isDisposed &&
|
||||
(file.manager.isInProject(file) || file is KtCodeFragment) &&
|
||||
(file is KtFile) &&
|
||||
isAvailable(project, editor, file)
|
||||
}
|
||||
finally {
|
||||
CREATEBYPATTERN_MAY_NOT_REFORMAT = false
|
||||
}
|
||||
}
|
||||
|
||||
protected open fun isAvailable(project: Project, editor: Editor?, file: KtFile): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
final override fun invoke(project: Project, editor: Editor?, file: PsiFile) {
|
||||
val element = element ?: return
|
||||
if (file is KtFile && FileModificationService.getInstance().prepareFileForWrite(element.containingFile)) {
|
||||
invoke(project, editor, file)
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract fun invoke(project: Project, editor: Editor?, file: KtFile)
|
||||
|
||||
override fun startInWriteAction() = true
|
||||
}
|
||||
-1135
File diff suppressed because it is too large
Load Diff
-250
@@ -1,250 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder
|
||||
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.util.ArrayUtil
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptorWithResolutionScopes
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny
|
||||
import org.jetbrains.kotlin.idea.core.KotlinNameSuggester
|
||||
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.createClass.ClassInfo
|
||||
import org.jetbrains.kotlin.idea.util.getResolutionScope
|
||||
import org.jetbrains.kotlin.idea.util.getResolvableApproximations
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils
|
||||
import org.jetbrains.kotlin.resolve.scopes.LexicalScope
|
||||
import org.jetbrains.kotlin.types.ErrorUtils
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.Variance
|
||||
import org.jetbrains.kotlin.types.typeUtil.makeNotNullable
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
* Represents a concrete type or a set of types yet to be inferred from an expression.
|
||||
*/
|
||||
abstract class TypeInfo(val variance: Variance) {
|
||||
object Empty: TypeInfo(Variance.INVARIANT) {
|
||||
override fun getPossibleTypes(builder: CallableBuilder): List<KotlinType> = Collections.emptyList()
|
||||
}
|
||||
|
||||
class ByExpression(val expression: KtExpression, variance: Variance): TypeInfo(variance) {
|
||||
override fun getPossibleNamesFromExpression(bindingContext: BindingContext): Array<String> {
|
||||
return KotlinNameSuggester.suggestNamesByExpressionOnly(expression, bindingContext, { true }).toTypedArray()
|
||||
}
|
||||
|
||||
override fun getPossibleTypes(builder: CallableBuilder): List<KotlinType> =
|
||||
expression.guessTypes(
|
||||
context = builder.currentFileContext,
|
||||
module = builder.currentFileModule,
|
||||
pseudocode = builder.pseudocode
|
||||
).flatMap { it.getPossibleSupertypes(variance, builder) }
|
||||
}
|
||||
|
||||
class ByTypeReference(val typeReference: KtTypeReference, variance: Variance): TypeInfo(variance) {
|
||||
override fun getPossibleTypes(builder: CallableBuilder): List<KotlinType> =
|
||||
builder.currentFileContext[BindingContext.TYPE, typeReference].getPossibleSupertypes(variance, builder)
|
||||
}
|
||||
|
||||
class ByType(val theType: KotlinType, variance: Variance): TypeInfo(variance) {
|
||||
override fun getPossibleTypes(builder: CallableBuilder): List<KotlinType> =
|
||||
theType.getPossibleSupertypes(variance, builder)
|
||||
}
|
||||
|
||||
class ByReceiverType(variance: Variance): TypeInfo(variance) {
|
||||
override fun getPossibleTypes(builder: CallableBuilder): List<KotlinType> =
|
||||
(builder.placement as CallablePlacement.WithReceiver).receiverTypeCandidate.theType.getPossibleSupertypes(variance, builder)
|
||||
}
|
||||
|
||||
abstract class DelegatingTypeInfo(val delegate: TypeInfo): TypeInfo(delegate.variance) {
|
||||
override val substitutionsAllowed: Boolean = delegate.substitutionsAllowed
|
||||
override fun getPossibleNamesFromExpression(bindingContext: BindingContext) = delegate.getPossibleNamesFromExpression(bindingContext)
|
||||
override fun getPossibleTypes(builder: CallableBuilder): List<KotlinType> = delegate.getPossibleTypes(builder)
|
||||
}
|
||||
|
||||
class NoSubstitutions(delegate: TypeInfo): DelegatingTypeInfo(delegate) {
|
||||
override val substitutionsAllowed: Boolean = false
|
||||
}
|
||||
|
||||
class StaticContextRequired(delegate: TypeInfo): DelegatingTypeInfo(delegate) {
|
||||
override val staticContextRequired: Boolean = true
|
||||
}
|
||||
|
||||
class OfThis(delegate: TypeInfo) : DelegatingTypeInfo(delegate)
|
||||
|
||||
val isOfThis: Boolean
|
||||
get() = when (this) {
|
||||
is OfThis -> true
|
||||
is DelegatingTypeInfo -> delegate.isOfThis
|
||||
else -> false
|
||||
}
|
||||
|
||||
open val substitutionsAllowed: Boolean = true
|
||||
open val staticContextRequired: Boolean = false
|
||||
open fun getPossibleNamesFromExpression(bindingContext: BindingContext): Array<String> = ArrayUtil.EMPTY_STRING_ARRAY
|
||||
abstract fun getPossibleTypes(builder: CallableBuilder): List<KotlinType>
|
||||
|
||||
private fun getScopeForTypeApproximation(config: CallableBuilderConfiguration, placement: CallablePlacement?): LexicalScope? {
|
||||
if (placement == null) return config.originalElement.getResolutionScope()
|
||||
|
||||
val containingElement = when (placement) {
|
||||
is CallablePlacement.NoReceiver -> {
|
||||
placement.containingElement
|
||||
}
|
||||
is CallablePlacement.WithReceiver -> {
|
||||
val receiverClassDescriptor =
|
||||
placement.receiverTypeCandidate.theType.constructor.declarationDescriptor
|
||||
val classDeclaration = receiverClassDescriptor?.let { DescriptorToSourceUtils.getSourceFromDescriptor(it) }
|
||||
if (!config.isExtension && classDeclaration != null) classDeclaration else config.currentFile
|
||||
}
|
||||
}
|
||||
return when (containingElement) {
|
||||
is KtClassOrObject -> (containingElement.resolveToDescriptorIfAny() as? ClassDescriptorWithResolutionScopes)?.scopeForMemberDeclarationResolution
|
||||
is KtBlockExpression -> (containingElement.statements.firstOrNull() ?: containingElement).getResolutionScope()
|
||||
is KtElement -> containingElement.containingKtFile.getResolutionScope()
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
protected fun KotlinType?.getPossibleSupertypes(variance: Variance, callableBuilder: CallableBuilder): List<KotlinType> {
|
||||
if (this == null || ErrorUtils.containsErrorType(this)) {
|
||||
return Collections.singletonList(callableBuilder.currentFileModule.builtIns.anyType)
|
||||
}
|
||||
val scope = getScopeForTypeApproximation(callableBuilder.config, callableBuilder.placement)
|
||||
val approximations = getResolvableApproximations(scope, false, true)
|
||||
return when (variance) {
|
||||
Variance.IN_VARIANCE -> approximations.toList()
|
||||
else -> listOf(approximations.firstOrNull() ?: this)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun TypeInfo(expressionOfType: KtExpression, variance: Variance): TypeInfo = TypeInfo.ByExpression(expressionOfType, variance)
|
||||
fun TypeInfo(typeReference: KtTypeReference, variance: Variance): TypeInfo = TypeInfo.ByTypeReference(typeReference, variance)
|
||||
fun TypeInfo(theType: KotlinType, variance: Variance): TypeInfo = TypeInfo.ByType(theType, variance)
|
||||
|
||||
fun TypeInfo.noSubstitutions(): TypeInfo = (this as? TypeInfo.NoSubstitutions) ?: TypeInfo.NoSubstitutions(this)
|
||||
|
||||
fun TypeInfo.forceNotNull(): TypeInfo {
|
||||
class ForcedNotNull(delegate: TypeInfo): TypeInfo.DelegatingTypeInfo(delegate) {
|
||||
override fun getPossibleTypes(builder: CallableBuilder): List<KotlinType> =
|
||||
super.getPossibleTypes(builder).map { it.makeNotNullable() }
|
||||
}
|
||||
|
||||
return (this as? ForcedNotNull) ?: ForcedNotNull(this)
|
||||
}
|
||||
|
||||
fun TypeInfo.ofThis() = TypeInfo.OfThis(this)
|
||||
|
||||
/**
|
||||
* Encapsulates information about a function parameter that is going to be created.
|
||||
*/
|
||||
class ParameterInfo(
|
||||
val typeInfo: TypeInfo,
|
||||
val preferredName: String? = null
|
||||
)
|
||||
|
||||
enum class CallableKind {
|
||||
FUNCTION,
|
||||
CLASS_WITH_PRIMARY_CONSTRUCTOR,
|
||||
CONSTRUCTOR,
|
||||
PROPERTY
|
||||
}
|
||||
|
||||
abstract class CallableInfo (
|
||||
val name: String,
|
||||
val receiverTypeInfo: TypeInfo,
|
||||
val returnTypeInfo: TypeInfo,
|
||||
val possibleContainers: List<KtElement>,
|
||||
val typeParameterInfos: List<TypeInfo>,
|
||||
val isAbstract: Boolean = false
|
||||
) {
|
||||
abstract val kind: CallableKind
|
||||
abstract val parameterInfos: List<ParameterInfo>
|
||||
|
||||
abstract fun copy(receiverTypeInfo: TypeInfo = this.receiverTypeInfo,
|
||||
possibleContainers: List<KtElement> = this.possibleContainers,
|
||||
isAbstract: Boolean = this.isAbstract): CallableInfo
|
||||
}
|
||||
|
||||
class FunctionInfo(name: String,
|
||||
receiverTypeInfo: TypeInfo,
|
||||
returnTypeInfo: TypeInfo,
|
||||
possibleContainers: List<KtElement> = Collections.emptyList(),
|
||||
override val parameterInfos: List<ParameterInfo> = Collections.emptyList(),
|
||||
typeParameterInfos: List<TypeInfo> = Collections.emptyList(),
|
||||
val isOperator: Boolean = false,
|
||||
val isInfix: Boolean = false,
|
||||
isAbstract: Boolean = false
|
||||
) : CallableInfo(name, receiverTypeInfo, returnTypeInfo, possibleContainers, typeParameterInfos, isAbstract) {
|
||||
override val kind: CallableKind get() = CallableKind.FUNCTION
|
||||
|
||||
override fun copy(receiverTypeInfo: TypeInfo, possibleContainers: List<KtElement>, isAbstract: Boolean) = FunctionInfo(
|
||||
name,
|
||||
receiverTypeInfo,
|
||||
returnTypeInfo,
|
||||
possibleContainers,
|
||||
parameterInfos,
|
||||
typeParameterInfos,
|
||||
isOperator,
|
||||
isInfix,
|
||||
isAbstract
|
||||
)
|
||||
}
|
||||
|
||||
class ClassWithPrimaryConstructorInfo(val classInfo: ClassInfo, expectedTypeInfo: TypeInfo): CallableInfo(
|
||||
classInfo.name, TypeInfo.Empty, expectedTypeInfo.forceNotNull(), Collections.emptyList(), classInfo.typeArguments, false
|
||||
) {
|
||||
override val kind: CallableKind get() = CallableKind.CLASS_WITH_PRIMARY_CONSTRUCTOR
|
||||
override val parameterInfos: List<ParameterInfo> get() = classInfo.parameterInfos
|
||||
|
||||
override fun copy(receiverTypeInfo: TypeInfo, possibleContainers: List<KtElement>, isAbstract: Boolean) = throw UnsupportedOperationException()
|
||||
}
|
||||
|
||||
class ConstructorInfo(
|
||||
override val parameterInfos: List<ParameterInfo>,
|
||||
val targetClass: PsiElement,
|
||||
val isPrimary: Boolean = false
|
||||
): CallableInfo("", TypeInfo.Empty, TypeInfo.Empty, Collections.emptyList(), Collections.emptyList(), false) {
|
||||
override val kind: CallableKind get() = CallableKind.CONSTRUCTOR
|
||||
|
||||
override fun copy(receiverTypeInfo: TypeInfo, possibleContainers: List<KtElement>, isAbstract: Boolean) = throw UnsupportedOperationException()
|
||||
}
|
||||
|
||||
class PropertyInfo(name: String,
|
||||
receiverTypeInfo: TypeInfo,
|
||||
returnTypeInfo: TypeInfo,
|
||||
val writable: Boolean,
|
||||
possibleContainers: List<KtElement> = Collections.emptyList(),
|
||||
typeParameterInfos: List<TypeInfo> = Collections.emptyList(),
|
||||
isAbstract: Boolean = false,
|
||||
val isLateinitPreferred: Boolean = false
|
||||
) : CallableInfo(name, receiverTypeInfo, returnTypeInfo, possibleContainers, typeParameterInfos, isAbstract) {
|
||||
override val kind: CallableKind get() = CallableKind.PROPERTY
|
||||
override val parameterInfos: List<ParameterInfo> get() = Collections.emptyList()
|
||||
|
||||
override fun copy(receiverTypeInfo: TypeInfo, possibleContainers: List<KtElement>, isAbstract: Boolean) = PropertyInfo(
|
||||
name,
|
||||
receiverTypeInfo,
|
||||
returnTypeInfo,
|
||||
writable,
|
||||
possibleContainers,
|
||||
typeParameterInfos,
|
||||
isAbstract,
|
||||
isLateinitPreferred
|
||||
)
|
||||
}
|
||||
-62
@@ -1,62 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder
|
||||
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.intellij.openapi.editor.ScrollType
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.PsiWhiteSpace
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.endOffset
|
||||
import org.jetbrains.kotlin.psi.psiUtil.siblings
|
||||
import org.jetbrains.kotlin.psi.psiUtil.startOffset
|
||||
|
||||
fun setupEditorSelection(editor: Editor, declaration: KtNamedDeclaration) {
|
||||
val caretModel = editor.caretModel
|
||||
val selectionModel = editor.selectionModel
|
||||
|
||||
if (declaration is KtSecondaryConstructor) {
|
||||
caretModel.moveToOffset(declaration.getConstructorKeyword().endOffset)
|
||||
}
|
||||
else {
|
||||
caretModel.moveToOffset(declaration.nameIdentifier!!.endOffset)
|
||||
}
|
||||
|
||||
fun positionBetween(left: PsiElement, right: PsiElement) {
|
||||
val from = left.siblings(withItself = false, forward = true).firstOrNull { it !is PsiWhiteSpace } ?: return
|
||||
val to = right.siblings(withItself = false, forward = false).firstOrNull { it !is PsiWhiteSpace } ?: return
|
||||
val startOffset = from.startOffset
|
||||
val endOffset = to.endOffset
|
||||
caretModel.moveToOffset(endOffset)
|
||||
selectionModel.setSelection(startOffset, endOffset)
|
||||
}
|
||||
|
||||
when (declaration) {
|
||||
is KtNamedFunction, is KtSecondaryConstructor -> {
|
||||
((declaration as KtFunction).bodyExpression as? KtBlockExpression)?.let {
|
||||
positionBetween(it.lBrace!!, it.rBrace!!)
|
||||
}
|
||||
}
|
||||
is KtClassOrObject -> {
|
||||
caretModel.moveToOffset(declaration.startOffset)
|
||||
}
|
||||
is KtProperty -> {
|
||||
caretModel.moveToOffset(declaration.endOffset)
|
||||
}
|
||||
}
|
||||
editor.scrollingModel.scrollToCaret(ScrollType.CENTER)
|
||||
}
|
||||
-62
@@ -1,62 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.quickfix.createFromUsage.createCallable
|
||||
|
||||
import org.jetbrains.kotlin.diagnostics.Diagnostic
|
||||
import org.jetbrains.kotlin.idea.project.builtIns
|
||||
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.*
|
||||
import org.jetbrains.kotlin.lexer.KtToken
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
import org.jetbrains.kotlin.psi.KtBinaryExpression
|
||||
import org.jetbrains.kotlin.types.Variance
|
||||
import org.jetbrains.kotlin.types.expressions.OperatorConventions
|
||||
import java.util.*
|
||||
|
||||
object CreateBinaryOperationActionFactory : CreateCallableMemberFromUsageFactory<KtBinaryExpression>() {
|
||||
override fun getElementOfInterest(diagnostic: Diagnostic): KtBinaryExpression? {
|
||||
return diagnostic.psiElement.parent as? KtBinaryExpression
|
||||
}
|
||||
|
||||
override fun createCallableInfo(element: KtBinaryExpression, diagnostic: Diagnostic): CallableInfo? {
|
||||
val token = element.operationToken as KtToken
|
||||
val operationName = when (token) {
|
||||
KtTokens.IDENTIFIER -> element.operationReference.getReferencedName()
|
||||
else -> OperatorConventions.getNameForOperationSymbol(token, false, true)?.asString()
|
||||
} ?: return null
|
||||
val inOperation = token in OperatorConventions.IN_OPERATIONS
|
||||
val comparisonOperation = token in OperatorConventions.COMPARISON_OPERATIONS
|
||||
|
||||
val leftExpr = element.left ?: return null
|
||||
val rightExpr = element.right ?: return null
|
||||
|
||||
val receiverExpr = if (inOperation) rightExpr else leftExpr
|
||||
val argumentExpr = if (inOperation) leftExpr else rightExpr
|
||||
|
||||
val builtIns = element.builtIns
|
||||
val receiverType = TypeInfo(receiverExpr, Variance.IN_VARIANCE)
|
||||
val returnType = when {
|
||||
inOperation -> TypeInfo.ByType(builtIns.booleanType, Variance.INVARIANT).noSubstitutions()
|
||||
comparisonOperation -> TypeInfo.ByType(builtIns.intType, Variance.INVARIANT).noSubstitutions()
|
||||
else -> TypeInfo(element, Variance.OUT_VARIANCE)
|
||||
}
|
||||
val parameters = Collections.singletonList(ParameterInfo(TypeInfo(argumentExpr, Variance.IN_VARIANCE)))
|
||||
val isOperator = token != KtTokens.IDENTIFIER
|
||||
return FunctionInfo(operationName, receiverType, returnType, parameterInfos = parameters,
|
||||
isOperator = isOperator,
|
||||
isInfix = !isOperator)
|
||||
}
|
||||
}
|
||||
-348
@@ -1,348 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2016 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.quickfix.createFromUsage.createCallable
|
||||
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.psi.PsiClass
|
||||
import com.intellij.psi.util.PsiTreeUtil
|
||||
import org.jetbrains.kotlin.analyzer.AnalysisResult
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ConstructorDescriptor
|
||||
import org.jetbrains.kotlin.diagnostics.Diagnostic
|
||||
import org.jetbrains.kotlin.diagnostics.Errors
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.analyzeAndGetResult
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.unsafeResolveToDescriptor
|
||||
import org.jetbrains.kotlin.idea.codeInsight.DescriptorToSourceUtilsIde
|
||||
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.*
|
||||
import org.jetbrains.kotlin.idea.refactoring.canRefactor
|
||||
import org.jetbrains.kotlin.idea.refactoring.getExtractionContainers
|
||||
import org.jetbrains.kotlin.idea.refactoring.isInterfaceClass
|
||||
import org.jetbrains.kotlin.idea.util.isAbstract
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
import org.jetbrains.kotlin.load.java.descriptors.JavaClassDescriptor
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.*
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.bindingContextUtil.getReferenceTargets
|
||||
import org.jetbrains.kotlin.resolve.calls.callUtil.getCall
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.classValueType
|
||||
import org.jetbrains.kotlin.resolve.scopes.receivers.*
|
||||
import org.jetbrains.kotlin.resolve.source.getPsi
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.TypeUtils
|
||||
import org.jetbrains.kotlin.types.Variance
|
||||
import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf
|
||||
import java.lang.AssertionError
|
||||
import java.util.*
|
||||
|
||||
sealed class CreateCallableFromCallActionFactory<E : KtExpression>(
|
||||
extensionsEnabled: Boolean = true
|
||||
) : CreateCallableMemberFromUsageFactory<E>(extensionsEnabled) {
|
||||
protected abstract fun doCreateCallableInfo(
|
||||
expression: E,
|
||||
analysisResult: AnalysisResult,
|
||||
name: String,
|
||||
receiverType: TypeInfo,
|
||||
possibleContainers: List<KtElement>
|
||||
): CallableInfo?
|
||||
|
||||
protected fun getExpressionOfInterest(diagnostic: Diagnostic): KtExpression? {
|
||||
val diagElement = diagnostic.psiElement
|
||||
if (PsiTreeUtil.getParentOfType(
|
||||
diagElement,
|
||||
KtTypeReference::class.java, KtAnnotationEntry::class.java, KtImportDirective::class.java
|
||||
) != null) return null
|
||||
|
||||
return when (diagnostic.factory) {
|
||||
in Errors.UNRESOLVED_REFERENCE_DIAGNOSTICS, Errors.EXPRESSION_EXPECTED_PACKAGE_FOUND -> {
|
||||
val parent = diagElement.parent
|
||||
if (parent is KtCallExpression && parent.calleeExpression == diagElement) parent else diagElement
|
||||
}
|
||||
|
||||
Errors.NO_VALUE_FOR_PARAMETER,
|
||||
Errors.TOO_MANY_ARGUMENTS,
|
||||
Errors.NONE_APPLICABLE -> diagElement.getNonStrictParentOfType<KtCallExpression>()
|
||||
|
||||
Errors.TYPE_MISMATCH -> (diagElement.parent as? KtValueArgument)?.getStrictParentOfType<KtCallExpression>()
|
||||
|
||||
else -> throw AssertionError("Unexpected diagnostic: ${diagnostic.factory}")
|
||||
} as? KtExpression
|
||||
}
|
||||
|
||||
override fun createCallableInfo(element: E, diagnostic: Diagnostic): CallableInfo? {
|
||||
val project = element.project
|
||||
|
||||
val calleeExpr = when (element) {
|
||||
is KtCallExpression -> element.calleeExpression
|
||||
is KtSimpleNameExpression -> element
|
||||
else -> null
|
||||
} as? KtSimpleNameExpression ?: return null
|
||||
|
||||
if (calleeExpr.getReferencedNameElementType() != KtTokens.IDENTIFIER) return null
|
||||
|
||||
val analysisResult = calleeExpr.analyzeAndGetResult()
|
||||
val receiver = element.getCall(analysisResult.bindingContext)?.explicitReceiver
|
||||
val receiverType = getReceiverTypeInfo(analysisResult.bindingContext, project, receiver) ?: return null
|
||||
|
||||
val possibleContainers =
|
||||
if (receiverType is TypeInfo.Empty) {
|
||||
val containers = with(element.getQualifiedExpressionForSelectorOrThis().getExtractionContainers()) {
|
||||
if (element is KtCallExpression) this else filter { it is KtClassBody || it is KtFile }
|
||||
}
|
||||
if (containers.isNotEmpty()) containers else return null
|
||||
}
|
||||
else Collections.emptyList()
|
||||
|
||||
return doCreateCallableInfo(element, analysisResult, calleeExpr.getReferencedName(), receiverType, possibleContainers)
|
||||
}
|
||||
|
||||
private fun getReceiverTypeInfo(context: BindingContext, project: Project, receiver: Receiver?): TypeInfo? {
|
||||
return when (receiver) {
|
||||
null -> TypeInfo.Empty
|
||||
is Qualifier -> {
|
||||
val qualifierType = context.getType(receiver.expression)
|
||||
if (qualifierType != null) return TypeInfo(qualifierType, Variance.IN_VARIANCE)
|
||||
|
||||
if (receiver !is ClassQualifier) return null
|
||||
val classifierType = receiver.descriptor.classValueType
|
||||
if (classifierType != null) return TypeInfo(classifierType, Variance.IN_VARIANCE)
|
||||
|
||||
val javaClassifier = receiver.descriptor as? JavaClassDescriptor ?: return null
|
||||
val javaClass = DescriptorToSourceUtilsIde.getAnyDeclaration(project, javaClassifier) as? PsiClass
|
||||
if (javaClass == null || !javaClass.canRefactor()) return null
|
||||
TypeInfo.StaticContextRequired(TypeInfo(javaClassifier.defaultType, Variance.IN_VARIANCE))
|
||||
}
|
||||
is ReceiverValue -> {
|
||||
val originalType = receiver.type
|
||||
val finalType = if (receiver is ExpressionReceiver) {
|
||||
getDataFlowAwareTypes(receiver.expression, context, originalType).firstOrNull() ?: originalType
|
||||
} else originalType
|
||||
TypeInfo(finalType, Variance.IN_VARIANCE)
|
||||
}
|
||||
else -> throw AssertionError("Unexpected receiver: $receiver")
|
||||
}
|
||||
}
|
||||
|
||||
protected fun getAbstractCallableInfo(mainCallable: CallableInfo, originalExpression: KtExpression): CallableInfo? {
|
||||
val receiverTypeInfo: TypeInfo
|
||||
val receiverType: KotlinType
|
||||
|
||||
val originalReceiverTypeInfo = mainCallable.receiverTypeInfo
|
||||
if (originalReceiverTypeInfo != TypeInfo.Empty) {
|
||||
if (originalReceiverTypeInfo !is TypeInfo.ByType) return null
|
||||
receiverTypeInfo = originalReceiverTypeInfo
|
||||
receiverType = receiverTypeInfo.theType
|
||||
}
|
||||
else {
|
||||
val containingClass = originalExpression.getStrictParentOfType<KtClassOrObject>() as? KtClass ?: return null
|
||||
if (containingClass is KtEnumEntry) return null
|
||||
receiverType = (containingClass.unsafeResolveToDescriptor() as ClassDescriptor).defaultType
|
||||
receiverTypeInfo = TypeInfo(receiverType, Variance.IN_VARIANCE).ofThis()
|
||||
}
|
||||
|
||||
if (!receiverType.isAbstract() && TypeUtils.getAllSupertypes(receiverType).all { !it.isAbstract() }) return null
|
||||
|
||||
return mainCallable.copy(receiverTypeInfo = receiverTypeInfo, possibleContainers = emptyList(), isAbstract = true)
|
||||
}
|
||||
|
||||
protected fun getCallableWithReceiverInsideExtension(
|
||||
mainCallable: CallableInfo,
|
||||
originalExpression: KtExpression,
|
||||
context: BindingContext,
|
||||
receiverType: TypeInfo
|
||||
): CallableInfo? {
|
||||
if (receiverType != TypeInfo.Empty) return null
|
||||
val callable = (originalExpression.getParentOfTypeAndBranch<KtFunction> { bodyExpression }
|
||||
?: originalExpression.getParentOfTypeAndBranches<KtProperty> { listOf(getter, setter) })
|
||||
?: return null
|
||||
if (callable !is KtFunctionLiteral && callable.receiverTypeReference == null) return null
|
||||
val callableDescriptor = context[BindingContext.DECLARATION_TO_DESCRIPTOR, callable] as? CallableDescriptor ?: return null
|
||||
val extensionReceiverType = callableDescriptor.extensionReceiverParameter?.type ?: return null
|
||||
val newReceiverTypeInfo = TypeInfo(extensionReceiverType, Variance.IN_VARIANCE)
|
||||
return mainCallable.copy(receiverTypeInfo = newReceiverTypeInfo, possibleContainers = emptyList())
|
||||
}
|
||||
|
||||
sealed class Property: CreateCallableFromCallActionFactory<KtSimpleNameExpression>() {
|
||||
override fun getElementOfInterest(diagnostic: Diagnostic): KtSimpleNameExpression? {
|
||||
val refExpr = getExpressionOfInterest(diagnostic) as? KtNameReferenceExpression ?: return null
|
||||
if (refExpr.getParentOfTypeAndBranch<KtCallableReferenceExpression> { callableReference } != null) return null
|
||||
return refExpr
|
||||
}
|
||||
|
||||
override fun doCreateCallableInfo(
|
||||
expression: KtSimpleNameExpression,
|
||||
analysisResult: AnalysisResult,
|
||||
name: String,
|
||||
receiverType: TypeInfo,
|
||||
possibleContainers: List<KtElement>
|
||||
): CallableInfo? {
|
||||
val fullCallExpr = expression.getQualifiedExpressionForSelectorOrThis()
|
||||
val varExpected = fullCallExpr.getAssignmentByLHS() != null
|
||||
val expressionForTypeGuess = fullCallExpr.getExpressionForTypeGuess()
|
||||
val returnTypes = expressionForTypeGuess.guessTypes(analysisResult.bindingContext, analysisResult.moduleDescriptor)
|
||||
val returnTypeInfo = TypeInfo(expressionForTypeGuess, if (varExpected) Variance.INVARIANT else Variance.OUT_VARIANCE)
|
||||
val canBeLateinit =
|
||||
varExpected
|
||||
&& returnTypes.any { !it.isMarkedNullable && !KotlinBuiltIns.isPrimitiveType(it) }
|
||||
&& fullCallExpr.parents.firstOrNull { it is KtDeclarationWithBody || it is KtClassInitializer } is KtDeclarationWithBody
|
||||
return PropertyInfo(name, receiverType, returnTypeInfo, varExpected, possibleContainers, isLateinitPreferred = canBeLateinit)
|
||||
}
|
||||
|
||||
object Default : Property() {
|
||||
override fun doCreateCallableInfo(
|
||||
expression: KtSimpleNameExpression,
|
||||
analysisResult: AnalysisResult,
|
||||
name: String,
|
||||
receiverType: TypeInfo,
|
||||
possibleContainers: List<KtElement>
|
||||
): CallableInfo? {
|
||||
return super.doCreateCallableInfo(
|
||||
expression,
|
||||
analysisResult,
|
||||
name,
|
||||
receiverType,
|
||||
possibleContainers.filterNot { it is KtClassBody && (it.parent as KtClassOrObject).isInterfaceClass() }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
object Abstract : Property() {
|
||||
override fun doCreateCallableInfo(
|
||||
expression: KtSimpleNameExpression,
|
||||
analysisResult: AnalysisResult,
|
||||
name: String,
|
||||
receiverType: TypeInfo,
|
||||
possibleContainers: List<KtElement>
|
||||
) = super.doCreateCallableInfo(expression, analysisResult, name, receiverType, possibleContainers)?.let {
|
||||
getAbstractCallableInfo(it, expression)
|
||||
}
|
||||
}
|
||||
|
||||
object ByImplicitExtensionReceiver : Property() {
|
||||
override fun doCreateCallableInfo(
|
||||
expression: KtSimpleNameExpression,
|
||||
analysisResult: AnalysisResult,
|
||||
name: String,
|
||||
receiverType: TypeInfo,
|
||||
possibleContainers: List<KtElement>
|
||||
) = super.doCreateCallableInfo(expression, analysisResult, name, receiverType, possibleContainers)?.let {
|
||||
ByImplicitExtensionReceiver.getCallableWithReceiverInsideExtension(it, expression, analysisResult.bindingContext, receiverType)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sealed class Function: CreateCallableFromCallActionFactory<KtCallExpression>() {
|
||||
override fun getElementOfInterest(diagnostic: Diagnostic): KtCallExpression? {
|
||||
return getExpressionOfInterest(diagnostic) as? KtCallExpression
|
||||
}
|
||||
|
||||
override fun doCreateCallableInfo(
|
||||
expression: KtCallExpression,
|
||||
analysisResult: AnalysisResult,
|
||||
name: String,
|
||||
receiverType: TypeInfo,
|
||||
possibleContainers: List<KtElement>
|
||||
): CallableInfo? {
|
||||
val parameters = expression.getParameterInfos()
|
||||
val typeParameters = expression.getTypeInfoForTypeArguments()
|
||||
val fullCallExpression = expression.getQualifiedExpressionForSelectorOrThis()
|
||||
val expectedType = fullCallExpression.guessTypes(analysisResult.bindingContext, analysisResult.moduleDescriptor).singleOrNull()
|
||||
val returnType = if (expectedType != null) {
|
||||
TypeInfo(expectedType, Variance.OUT_VARIANCE)
|
||||
} else {
|
||||
TypeInfo(fullCallExpression, Variance.OUT_VARIANCE)
|
||||
}
|
||||
return FunctionInfo(name, receiverType, returnType, possibleContainers, parameters, typeParameters)
|
||||
}
|
||||
|
||||
object Default : Function()
|
||||
|
||||
object Abstract : Function() {
|
||||
override fun doCreateCallableInfo(
|
||||
expression: KtCallExpression,
|
||||
analysisResult: AnalysisResult,
|
||||
name: String,
|
||||
receiverType: TypeInfo,
|
||||
possibleContainers: List<KtElement>
|
||||
) = super.doCreateCallableInfo(expression, analysisResult, name, receiverType, possibleContainers)?.let {
|
||||
getAbstractCallableInfo(it, expression)
|
||||
}
|
||||
}
|
||||
|
||||
object ByImplicitExtensionReceiver : Function() {
|
||||
override fun doCreateCallableInfo(
|
||||
expression: KtCallExpression,
|
||||
analysisResult: AnalysisResult,
|
||||
name: String,
|
||||
receiverType: TypeInfo,
|
||||
possibleContainers: List<KtElement>
|
||||
) = super.doCreateCallableInfo(expression, analysisResult, name, receiverType, possibleContainers)?.let {
|
||||
getCallableWithReceiverInsideExtension(it, expression, analysisResult.bindingContext, receiverType)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
object Constructor: CreateCallableFromCallActionFactory<KtCallExpression>() {
|
||||
override fun getElementOfInterest(diagnostic: Diagnostic): KtCallExpression? {
|
||||
return getExpressionOfInterest(diagnostic) as? KtCallExpression
|
||||
}
|
||||
|
||||
override fun doCreateCallableInfo(
|
||||
expression: KtCallExpression,
|
||||
analysisResult: AnalysisResult,
|
||||
name: String,
|
||||
receiverType: TypeInfo,
|
||||
possibleContainers: List<KtElement>
|
||||
): CallableInfo? {
|
||||
if (expression.typeArguments.isNotEmpty()) return null
|
||||
|
||||
val classDescriptor = expression
|
||||
.calleeExpression
|
||||
?.getReferenceTargets(analysisResult.bindingContext)
|
||||
?.mapNotNull { (it as? ConstructorDescriptor)?.containingDeclaration }
|
||||
?.distinct()
|
||||
?.singleOrNull() as? ClassDescriptor
|
||||
val klass = classDescriptor?.source?.getPsi()
|
||||
if ((klass !is KtClass && klass !is PsiClass) || !klass.canRefactor()) return null
|
||||
|
||||
val expectedType = analysisResult.bindingContext[BindingContext.EXPECTED_EXPRESSION_TYPE, expression.getQualifiedExpressionForSelectorOrThis()]
|
||||
?: classDescriptor.builtIns.nullableAnyType
|
||||
if (!classDescriptor.defaultType.isSubtypeOf(expectedType)) return null
|
||||
|
||||
val parameters = expression.getParameterInfos()
|
||||
|
||||
return ConstructorInfo(parameters, klass)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
val FUNCTIONS = arrayOf(Function.Default,
|
||||
Function.Abstract,
|
||||
Function.ByImplicitExtensionReceiver,
|
||||
Constructor)
|
||||
val INSTANCES = arrayOf(Function.Default,
|
||||
Function.Abstract,
|
||||
Function.ByImplicitExtensionReceiver,
|
||||
Constructor,
|
||||
Property.Default,
|
||||
Property.Abstract,
|
||||
Property.ByImplicitExtensionReceiver)
|
||||
}
|
||||
}
|
||||
-216
@@ -1,216 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.quickfix.createFromUsage.createCallable
|
||||
|
||||
import com.intellij.codeInsight.intention.LowPriorityAction
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.psi.PsiClass
|
||||
import com.intellij.psi.PsiElement
|
||||
import org.jetbrains.kotlin.builtins.functions.FunctionClassDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ClassifierDescriptor
|
||||
import org.jetbrains.kotlin.idea.codeInsight.DescriptorToSourceUtilsIde
|
||||
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.CreateFromUsageFixBase
|
||||
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.*
|
||||
import org.jetbrains.kotlin.idea.refactoring.canRefactor
|
||||
import org.jetbrains.kotlin.idea.refactoring.chooseContainerElementIfNecessary
|
||||
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers
|
||||
import org.jetbrains.kotlin.idea.util.application.executeCommand
|
||||
import org.jetbrains.kotlin.idea.util.isAbstract
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.getElementTextWithContext
|
||||
import java.util.*
|
||||
|
||||
class CreateCallableFromUsageFix<E : KtElement>(
|
||||
originalExpression: E,
|
||||
callableInfos: List<CallableInfo>
|
||||
) : CreateCallableFromUsageFixBase<E>(originalExpression, callableInfos, false)
|
||||
|
||||
class CreateExtensionCallableFromUsageFix<E : KtElement>(
|
||||
originalExpression: E,
|
||||
callableInfos: List<CallableInfo>
|
||||
) : CreateCallableFromUsageFixBase<E>(originalExpression, callableInfos, true), LowPriorityAction
|
||||
|
||||
abstract class CreateCallableFromUsageFixBase<E : KtElement>(
|
||||
originalExpression: E,
|
||||
private val callableInfos: List<CallableInfo>,
|
||||
val isExtension: Boolean
|
||||
) : CreateFromUsageFixBase<E>(originalExpression) {
|
||||
init {
|
||||
assert (callableInfos.isNotEmpty()) { "No CallableInfos: ${originalExpression.getElementTextWithContext()}" }
|
||||
if (callableInfos.size > 1) {
|
||||
val receiverSet = callableInfos.mapTo(HashSet<TypeInfo>()) { it.receiverTypeInfo }
|
||||
if (receiverSet.size > 1) throw AssertionError("All functions must have common receiver: $receiverSet")
|
||||
|
||||
val possibleContainerSet = callableInfos.mapTo(HashSet<List<KtElement>>()) { it.possibleContainers }
|
||||
if (possibleContainerSet.size > 1) throw AssertionError("All functions must have common containers: $possibleContainerSet")
|
||||
}
|
||||
}
|
||||
|
||||
private fun getDeclaration(descriptor: ClassifierDescriptor, project: Project): PsiElement? {
|
||||
if (descriptor is FunctionClassDescriptor) {
|
||||
val psiFactory = KtPsiFactory(project)
|
||||
val syntheticClass = psiFactory.createClass(IdeDescriptorRenderers.SOURCE_CODE_SHORT_NAMES_NO_ANNOTATIONS.render(descriptor))
|
||||
return psiFactory.createAnalyzableFile("${descriptor.name.asString()}.kt", "", element!!).add(syntheticClass)
|
||||
}
|
||||
return DescriptorToSourceUtilsIde.getAnyDeclaration(project, descriptor)
|
||||
}
|
||||
|
||||
private fun getDeclarationIfApplicable(project: Project, candidate: TypeCandidate): PsiElement? {
|
||||
val descriptor = candidate.theType.constructor.declarationDescriptor ?: return null
|
||||
val declaration = getDeclaration(descriptor, project) ?: return null
|
||||
if (declaration !is KtClassOrObject && declaration !is KtTypeParameter && declaration !is PsiClass) return null
|
||||
return if (isExtension || declaration.canRefactor()) declaration else null
|
||||
}
|
||||
|
||||
override fun getText(): String {
|
||||
val element = element ?: return ""
|
||||
val receiverTypeInfo = callableInfos.first().receiverTypeInfo
|
||||
val renderedCallables = callableInfos.map {
|
||||
buildString {
|
||||
if (it.isAbstract) {
|
||||
append("abstract ")
|
||||
}
|
||||
|
||||
val kind = when (it.kind) {
|
||||
CallableKind.FUNCTION -> "function"
|
||||
CallableKind.PROPERTY -> "property"
|
||||
CallableKind.CONSTRUCTOR -> "secondary constructor"
|
||||
else -> throw AssertionError("Unexpected callable info: $it")
|
||||
}
|
||||
append(kind)
|
||||
|
||||
if (it.name.isNotEmpty()) {
|
||||
append(" '")
|
||||
|
||||
val receiverType = if (!receiverTypeInfo.isOfThis) {
|
||||
CallableBuilderConfiguration(callableInfos, element, isExtension = isExtension)
|
||||
.createBuilder()
|
||||
.computeTypeCandidates(receiverTypeInfo)
|
||||
.firstOrNull()
|
||||
?.theType
|
||||
}
|
||||
else null
|
||||
|
||||
if (receiverType != null) {
|
||||
if (isExtension) {
|
||||
val receiverTypeText = IdeDescriptorRenderers.SOURCE_CODE_SHORT_NAMES_NO_ANNOTATIONS.renderType(receiverType)
|
||||
val isFunctionType = receiverType.constructor.declarationDescriptor is FunctionClassDescriptor
|
||||
append(if (isFunctionType) "($receiverTypeText)" else receiverTypeText).append('.')
|
||||
}
|
||||
else {
|
||||
receiverType.constructor.declarationDescriptor?.let {
|
||||
append(IdeDescriptorRenderers.SOURCE_CODE_SHORT_NAMES_NO_ANNOTATIONS.renderClassifierName(it)).append('.')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
append("${it.name}'")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return StringBuilder().apply {
|
||||
append("Create ")
|
||||
|
||||
val receiverInfo = receiverTypeInfo
|
||||
if (!callableInfos.any { it.isAbstract }) {
|
||||
if (isExtension) {
|
||||
append("extension ")
|
||||
}
|
||||
else if (receiverInfo !is TypeInfo.Empty) {
|
||||
append("member ")
|
||||
}
|
||||
}
|
||||
|
||||
renderedCallables.joinTo(this)
|
||||
}.toString()
|
||||
}
|
||||
|
||||
override fun isAvailable(project: Project, editor: Editor?, file: KtFile): Boolean {
|
||||
val element = element ?: return false
|
||||
|
||||
val receiverInfo = callableInfos.first().receiverTypeInfo
|
||||
|
||||
if (receiverInfo is TypeInfo.Empty) {
|
||||
if (callableInfos.any { it is PropertyInfo && it.possibleContainers.isEmpty() }) return false
|
||||
return !isExtension
|
||||
}
|
||||
// TODO: Remove after companion object extensions are supported
|
||||
if (isExtension && receiverInfo.staticContextRequired) return false
|
||||
|
||||
val callableBuilder = CallableBuilderConfiguration(callableInfos, element, isExtension = isExtension).createBuilder()
|
||||
val receiverTypeCandidates = callableBuilder.computeTypeCandidates(callableInfos.first().receiverTypeInfo)
|
||||
val propertyInfo = callableInfos.firstOrNull { it is PropertyInfo } as PropertyInfo?
|
||||
val isFunction = callableInfos.any { it.kind == CallableKind.FUNCTION }
|
||||
return receiverTypeCandidates.any {
|
||||
val declaration = getDeclarationIfApplicable(project, it)
|
||||
val insertToJavaInterface = declaration is PsiClass && declaration.isInterface
|
||||
when {
|
||||
!isExtension && propertyInfo != null && insertToJavaInterface && (!receiverInfo.staticContextRequired || propertyInfo.writable) ->
|
||||
false
|
||||
isFunction && insertToJavaInterface && receiverInfo.staticContextRequired ->
|
||||
false
|
||||
!isExtension && declaration is KtTypeParameter -> false
|
||||
propertyInfo != null && !propertyInfo.isAbstract && declaration is KtClass && declaration.isInterface() -> false
|
||||
else ->
|
||||
declaration != null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun invoke(project: Project, editor: Editor?, file: KtFile) {
|
||||
val element = element ?: return
|
||||
val callableInfo = callableInfos.first()
|
||||
|
||||
val callableBuilder =
|
||||
CallableBuilderConfiguration(callableInfos, element as KtElement, file, editor!!, isExtension).createBuilder()
|
||||
|
||||
fun runBuilder(placement: CallablePlacement) {
|
||||
callableBuilder.placement = placement
|
||||
project.executeCommand(text) { callableBuilder.build() }
|
||||
}
|
||||
|
||||
if (callableInfo is ConstructorInfo) {
|
||||
runBuilder(CallablePlacement.NoReceiver(callableInfo.targetClass))
|
||||
return
|
||||
}
|
||||
|
||||
val popupTitle = "Choose target class or interface"
|
||||
val receiverTypeCandidates = callableBuilder.computeTypeCandidates(callableInfo.receiverTypeInfo).let {
|
||||
if (callableInfo.isAbstract) it.filter { it.theType.isAbstract() } else it
|
||||
}
|
||||
if (receiverTypeCandidates.isNotEmpty()) {
|
||||
val containers = receiverTypeCandidates
|
||||
.mapNotNull { candidate -> getDeclarationIfApplicable(project, candidate)?.let { candidate to it } }
|
||||
|
||||
chooseContainerElementIfNecessary(containers, editor, popupTitle, false, { it.second }) {
|
||||
runBuilder(CallablePlacement.WithReceiver(it.first))
|
||||
}
|
||||
}
|
||||
else {
|
||||
assert(callableInfo.receiverTypeInfo is TypeInfo.Empty) {
|
||||
"No receiver type candidates: ${element.text} in ${file.text}"
|
||||
}
|
||||
|
||||
chooseContainerElementIfNecessary(callableInfo.possibleContainers, editor, popupTitle, true, { it }) {
|
||||
val container = if (it is KtClassBody) it.parent as KtClassOrObject else it
|
||||
runBuilder(CallablePlacement.NoReceiver(container))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
-78
@@ -1,78 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.quickfix.createFromUsage.createCallable
|
||||
|
||||
import com.intellij.psi.SmartPsiElementPointer
|
||||
import org.jetbrains.kotlin.diagnostics.Diagnostic
|
||||
import org.jetbrains.kotlin.idea.quickfix.IntentionActionPriority
|
||||
import org.jetbrains.kotlin.idea.quickfix.KotlinIntentionActionFactoryWithDelegate
|
||||
import org.jetbrains.kotlin.idea.quickfix.QuickFixWithDelegateFactory
|
||||
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.CreateFromUsageFixBase
|
||||
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.CallableInfo
|
||||
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.PropertyInfo
|
||||
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.createVariable.CreateParameterFromUsageFix
|
||||
import org.jetbrains.kotlin.psi.KtElement
|
||||
import java.util.*
|
||||
|
||||
abstract class CreateCallableMemberFromUsageFactory<E : KtElement>(
|
||||
private val extensionsSupported: Boolean = true
|
||||
) : KotlinIntentionActionFactoryWithDelegate<E, List<CallableInfo>>() {
|
||||
private fun newCallableQuickFix(
|
||||
originalElementPointer: SmartPsiElementPointer<E>,
|
||||
priority: IntentionActionPriority,
|
||||
quickFixDataFactory: () -> List<CallableInfo>?,
|
||||
quickFixFactory: (E, List<CallableInfo>) -> CreateFromUsageFixBase<E>?
|
||||
): QuickFixWithDelegateFactory {
|
||||
return QuickFixWithDelegateFactory(priority) {
|
||||
val data = quickFixDataFactory().orEmpty()
|
||||
val originalElement = originalElementPointer.element
|
||||
if (data.isNotEmpty() && originalElement != null) quickFixFactory(originalElement, data) else null
|
||||
}
|
||||
}
|
||||
|
||||
protected open fun createCallableInfo(element: E, diagnostic: Diagnostic): CallableInfo? = null
|
||||
|
||||
override fun extractFixData(element: E, diagnostic: Diagnostic): List<CallableInfo>
|
||||
= listOfNotNull(createCallableInfo(element, diagnostic))
|
||||
|
||||
override fun createFixes(
|
||||
originalElementPointer: SmartPsiElementPointer<E>,
|
||||
diagnostic: Diagnostic,
|
||||
quickFixDataFactory: () -> List<CallableInfo>?
|
||||
): List<QuickFixWithDelegateFactory> {
|
||||
val fixes = ArrayList<QuickFixWithDelegateFactory>(3)
|
||||
|
||||
newCallableQuickFix(originalElementPointer, IntentionActionPriority.NORMAL, quickFixDataFactory) { element, data ->
|
||||
CreateCallableFromUsageFix(element, data)
|
||||
}.let { fixes.add(it) }
|
||||
|
||||
newCallableQuickFix(originalElementPointer, IntentionActionPriority.NORMAL, quickFixDataFactory) f@ { element, data ->
|
||||
(data.singleOrNull() as? PropertyInfo)?.let {
|
||||
CreateParameterFromUsageFix.createFixForPrimaryConstructorPropertyParameter(element, it)
|
||||
}
|
||||
}.let { fixes.add(it) }
|
||||
|
||||
if (extensionsSupported) {
|
||||
newCallableQuickFix(originalElementPointer, IntentionActionPriority.LOW, quickFixDataFactory) { element, data ->
|
||||
if (data.any { it.isAbstract }) return@newCallableQuickFix null
|
||||
CreateExtensionCallableFromUsageFix(element, data)
|
||||
}.let { fixes.add(it) }
|
||||
}
|
||||
|
||||
return fixes
|
||||
}
|
||||
}
|
||||
-57
@@ -1,57 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.quickfix.createFromUsage.createCallable
|
||||
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.diagnostics.Diagnostic
|
||||
import org.jetbrains.kotlin.diagnostics.Errors
|
||||
import org.jetbrains.kotlin.idea.core.quickfix.QuickFixUtil
|
||||
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.CallableInfo
|
||||
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.FunctionInfo
|
||||
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.TypeInfo
|
||||
import org.jetbrains.kotlin.psi.KtDestructuringDeclaration
|
||||
import org.jetbrains.kotlin.psi.KtForExpression
|
||||
import org.jetbrains.kotlin.resolve.DataClassDescriptorResolver
|
||||
import org.jetbrains.kotlin.types.Variance
|
||||
|
||||
object CreateComponentFunctionActionFactory : CreateCallableMemberFromUsageFactory<KtDestructuringDeclaration>() {
|
||||
override fun getElementOfInterest(diagnostic: Diagnostic): KtDestructuringDeclaration? {
|
||||
QuickFixUtil.getParentElementOfType(diagnostic, KtDestructuringDeclaration::class.java)?.let { return it }
|
||||
return QuickFixUtil.getParentElementOfType(diagnostic, KtForExpression::class.java)?.destructuringDeclaration
|
||||
}
|
||||
|
||||
override fun createCallableInfo(element: KtDestructuringDeclaration, diagnostic: Diagnostic): CallableInfo? {
|
||||
val diagnosticWithParameters = Errors.COMPONENT_FUNCTION_MISSING.cast(diagnostic)
|
||||
|
||||
val name = diagnosticWithParameters.a
|
||||
if (!DataClassDescriptorResolver.isComponentLike(name)) return null
|
||||
|
||||
val componentNumber = DataClassDescriptorResolver.getComponentIndex(name.asString()) - 1
|
||||
|
||||
val targetType = diagnosticWithParameters.b
|
||||
val targetClassDescriptor = targetType.constructor.declarationDescriptor as? ClassDescriptor
|
||||
if (targetClassDescriptor != null && targetClassDescriptor.isData) return null
|
||||
|
||||
val ownerTypeInfo = TypeInfo(targetType, Variance.IN_VARIANCE)
|
||||
val entries = element.entries
|
||||
|
||||
val entry = entries[componentNumber]
|
||||
val returnTypeInfo = TypeInfo(entry, Variance.OUT_VARIANCE)
|
||||
|
||||
return FunctionInfo(name.identifier, ownerTypeInfo, returnTypeInfo, isOperator = true)
|
||||
}
|
||||
}
|
||||
-40
@@ -1,40 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.quickfix.createFromUsage.createCallable
|
||||
|
||||
import org.jetbrains.kotlin.diagnostics.Diagnostic
|
||||
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.CallableInfo
|
||||
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.FunctionInfo
|
||||
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.ParameterInfo
|
||||
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.TypeInfo
|
||||
import org.jetbrains.kotlin.psi.KtArrayAccessExpression
|
||||
import org.jetbrains.kotlin.types.Variance
|
||||
import org.jetbrains.kotlin.util.OperatorNameConventions
|
||||
import java.util.*
|
||||
|
||||
object CreateGetFunctionActionFactory : CreateGetSetFunctionActionFactory(isGet = true) {
|
||||
override fun createCallableInfo(element: KtArrayAccessExpression, diagnostic: Diagnostic): CallableInfo? {
|
||||
val arrayExpr = element.arrayExpression ?: return null
|
||||
|
||||
val arrayType = TypeInfo(arrayExpr, Variance.IN_VARIANCE)
|
||||
val parameters = element.indexExpressions.map { ParameterInfo(TypeInfo(it, Variance.IN_VARIANCE)) }
|
||||
val returnType = TypeInfo(element, Variance.OUT_VARIANCE)
|
||||
return FunctionInfo(
|
||||
OperatorNameConventions.GET.asString(), arrayType, returnType, Collections.emptyList(), parameters, isOperator = true
|
||||
)
|
||||
}
|
||||
}
|
||||
-43
@@ -1,43 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.quickfix.createFromUsage.createCallable
|
||||
|
||||
import org.jetbrains.kotlin.diagnostics.Diagnostic
|
||||
import org.jetbrains.kotlin.diagnostics.DiagnosticFactory
|
||||
import org.jetbrains.kotlin.diagnostics.Errors
|
||||
import org.jetbrains.kotlin.idea.core.quickfix.QuickFixUtil
|
||||
import org.jetbrains.kotlin.idea.project.builtIns
|
||||
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.CallableInfo
|
||||
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.FunctionInfo
|
||||
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.TypeInfo
|
||||
import org.jetbrains.kotlin.psi.KtForExpression
|
||||
import org.jetbrains.kotlin.types.Variance
|
||||
import org.jetbrains.kotlin.util.OperatorNameConventions
|
||||
|
||||
object CreateHasNextFunctionActionFactory : CreateCallableMemberFromUsageFactory<KtForExpression>() {
|
||||
override fun getElementOfInterest(diagnostic: Diagnostic): KtForExpression? {
|
||||
return QuickFixUtil.getParentElementOfType(diagnostic, KtForExpression::class.java)
|
||||
}
|
||||
|
||||
override fun createCallableInfo(element: KtForExpression, diagnostic: Diagnostic): CallableInfo? {
|
||||
val diagnosticWithParameters =
|
||||
DiagnosticFactory.cast(diagnostic, Errors.HAS_NEXT_MISSING, Errors.HAS_NEXT_FUNCTION_NONE_APPLICABLE)
|
||||
val ownerType = TypeInfo(diagnosticWithParameters.a, Variance.IN_VARIANCE)
|
||||
val returnType = TypeInfo(element.builtIns.booleanType, Variance.OUT_VARIANCE)
|
||||
return FunctionInfo(OperatorNameConventions.HAS_NEXT.asString(), ownerType, returnType, isOperator = true)
|
||||
}
|
||||
}
|
||||
-53
@@ -1,53 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.quickfix.createFromUsage.createCallable
|
||||
|
||||
import org.jetbrains.kotlin.diagnostics.Diagnostic
|
||||
import org.jetbrains.kotlin.diagnostics.Errors
|
||||
import org.jetbrains.kotlin.idea.project.builtIns
|
||||
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.CallableInfo
|
||||
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.FunctionInfo
|
||||
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.ParameterInfo
|
||||
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.TypeInfo
|
||||
import org.jetbrains.kotlin.psi.KtCallExpression
|
||||
import org.jetbrains.kotlin.types.Variance
|
||||
import org.jetbrains.kotlin.types.isError
|
||||
import org.jetbrains.kotlin.util.OperatorNameConventions
|
||||
|
||||
object CreateInvokeFunctionActionFactory : CreateCallableMemberFromUsageFactory<KtCallExpression>() {
|
||||
override fun getElementOfInterest(diagnostic: Diagnostic): KtCallExpression? {
|
||||
return diagnostic.psiElement.parent as? KtCallExpression
|
||||
}
|
||||
|
||||
override fun createCallableInfo(element: KtCallExpression, diagnostic: Diagnostic): CallableInfo? {
|
||||
val expectedType = Errors.FUNCTION_EXPECTED.cast(diagnostic).b
|
||||
if (expectedType.isError) return null
|
||||
|
||||
val receiverType = TypeInfo(expectedType, Variance.IN_VARIANCE)
|
||||
|
||||
val anyType = element.builtIns.nullableAnyType
|
||||
val parameters = element.valueArguments.map {
|
||||
ParameterInfo(
|
||||
it.getArgumentExpression()?.let { TypeInfo(it, Variance.IN_VARIANCE) } ?: TypeInfo(anyType, Variance.IN_VARIANCE),
|
||||
it.getArgumentName()?.referenceExpression?.getReferencedName()
|
||||
)
|
||||
}
|
||||
|
||||
val returnType = TypeInfo(element, Variance.OUT_VARIANCE)
|
||||
return FunctionInfo(OperatorNameConventions.INVOKE.asString(), receiverType, returnType, parameterInfos = parameters, isOperator = true)
|
||||
}
|
||||
}
|
||||
-62
@@ -1,62 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2016 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.quickfix.createFromUsage.createCallable
|
||||
|
||||
import org.jetbrains.kotlin.diagnostics.Diagnostic
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.analyzeWithAllCompilerChecks
|
||||
import org.jetbrains.kotlin.idea.core.quickfix.QuickFixUtil
|
||||
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.CallableInfo
|
||||
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.FunctionInfo
|
||||
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.TypeInfo
|
||||
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.guessTypes
|
||||
import org.jetbrains.kotlin.psi.KtExpression
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.psi.KtForExpression
|
||||
import org.jetbrains.kotlin.types.KotlinTypeFactory
|
||||
import org.jetbrains.kotlin.types.TypeProjectionImpl
|
||||
import org.jetbrains.kotlin.types.Variance
|
||||
import org.jetbrains.kotlin.util.OperatorNameConventions
|
||||
import java.util.*
|
||||
|
||||
object CreateIteratorFunctionActionFactory : CreateCallableMemberFromUsageFactory<KtForExpression>() {
|
||||
override fun getElementOfInterest(diagnostic: Diagnostic): KtForExpression? {
|
||||
return QuickFixUtil.getParentElementOfType(diagnostic, KtForExpression::class.java)
|
||||
}
|
||||
|
||||
override fun createCallableInfo(element: KtForExpression, diagnostic: Diagnostic): CallableInfo? {
|
||||
val file = diagnostic.psiFile as? KtFile ?: return null
|
||||
val iterableExpr = element.loopRange ?: return null
|
||||
val variableExpr: KtExpression = ((element.loopParameter ?: element.destructuringDeclaration) ?: return null) as KtExpression
|
||||
val iterableType = TypeInfo(iterableExpr, Variance.IN_VARIANCE)
|
||||
|
||||
val (bindingContext, moduleDescriptor) = file.analyzeWithAllCompilerChecks()
|
||||
|
||||
val returnJetType = moduleDescriptor.builtIns.iterator.defaultType
|
||||
val returnJetTypeParameterTypes = variableExpr.guessTypes(bindingContext, moduleDescriptor)
|
||||
if (returnJetTypeParameterTypes.size != 1) return null
|
||||
|
||||
val returnJetTypeParameterType = TypeProjectionImpl(returnJetTypeParameterTypes[0])
|
||||
val returnJetTypeArguments = Collections.singletonList(returnJetTypeParameterType)
|
||||
val newReturnJetType = KotlinTypeFactory.simpleTypeWithNonTrivialMemberScope(returnJetType.annotations,
|
||||
returnJetType.constructor,
|
||||
returnJetTypeArguments,
|
||||
returnJetType.isMarkedNullable,
|
||||
returnJetType.memberScope)
|
||||
val returnType = TypeInfo(newReturnJetType, Variance.OUT_VARIANCE)
|
||||
return FunctionInfo(OperatorNameConventions.ITERATOR.asString(), iterableType, returnType, isOperator = true)
|
||||
}
|
||||
}
|
||||
-44
@@ -1,44 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.quickfix.createFromUsage.createCallable
|
||||
|
||||
import org.jetbrains.kotlin.diagnostics.Diagnostic
|
||||
import org.jetbrains.kotlin.diagnostics.DiagnosticFactory
|
||||
import org.jetbrains.kotlin.diagnostics.Errors
|
||||
import org.jetbrains.kotlin.idea.core.quickfix.QuickFixUtil
|
||||
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.CallableInfo
|
||||
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.FunctionInfo
|
||||
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.TypeInfo
|
||||
import org.jetbrains.kotlin.psi.KtExpression
|
||||
import org.jetbrains.kotlin.psi.KtForExpression
|
||||
import org.jetbrains.kotlin.types.Variance
|
||||
import org.jetbrains.kotlin.util.OperatorNameConventions
|
||||
|
||||
object CreateNextFunctionActionFactory : CreateCallableMemberFromUsageFactory<KtForExpression>() {
|
||||
override fun getElementOfInterest(diagnostic: Diagnostic): KtForExpression? {
|
||||
return QuickFixUtil.getParentElementOfType(diagnostic, KtForExpression::class.java)
|
||||
}
|
||||
|
||||
override fun createCallableInfo(element: KtForExpression, diagnostic: Diagnostic): CallableInfo? {
|
||||
val diagnosticWithParameters = DiagnosticFactory.cast(diagnostic, Errors.NEXT_MISSING, Errors.NEXT_NONE_APPLICABLE)
|
||||
val ownerType = TypeInfo(diagnosticWithParameters.a, Variance.IN_VARIANCE)
|
||||
|
||||
val variableExpr = element.loopParameter ?: element.destructuringDeclaration ?: return null
|
||||
val returnType = TypeInfo(variableExpr as KtExpression, Variance.OUT_VARIANCE)
|
||||
return FunctionInfo(OperatorNameConventions.NEXT.asString(), ownerType, returnType, isOperator = true)
|
||||
}
|
||||
}
|
||||
-97
@@ -1,97 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.quickfix.createFromUsage.createCallable
|
||||
|
||||
import com.intellij.util.SmartList
|
||||
import org.jetbrains.kotlin.builtins.ReflectionTypes
|
||||
import org.jetbrains.kotlin.config.LanguageFeature
|
||||
import org.jetbrains.kotlin.descriptors.VariableAccessorDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.VariableDescriptorWithAccessors
|
||||
import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor
|
||||
import org.jetbrains.kotlin.diagnostics.Diagnostic
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
||||
import org.jetbrains.kotlin.idea.project.languageVersionSettings
|
||||
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.CallableInfo
|
||||
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.FunctionInfo
|
||||
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.ParameterInfo
|
||||
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.TypeInfo
|
||||
import org.jetbrains.kotlin.psi.KtExpression
|
||||
import org.jetbrains.kotlin.psi.KtProperty
|
||||
import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.module
|
||||
import org.jetbrains.kotlin.types.Variance
|
||||
import org.jetbrains.kotlin.util.OperatorNameConventions
|
||||
|
||||
object CreatePropertyDelegateAccessorsActionFactory : CreateCallableMemberFromUsageFactory<KtExpression>() {
|
||||
override fun getElementOfInterest(diagnostic: Diagnostic): KtExpression? {
|
||||
return diagnostic.psiElement as? KtExpression
|
||||
}
|
||||
|
||||
override fun extractFixData(element: KtExpression, diagnostic: Diagnostic): List<CallableInfo> {
|
||||
val context = element.analyze()
|
||||
|
||||
fun isApplicableForAccessor(accessor: VariableAccessorDescriptor?): Boolean =
|
||||
accessor != null && context[BindingContext.DELEGATED_PROPERTY_RESOLVED_CALL, accessor] == null
|
||||
|
||||
val property = element.getNonStrictParentOfType<KtProperty>() ?: return emptyList()
|
||||
val propertyDescriptor = context[BindingContext.DECLARATION_TO_DESCRIPTOR, property] as? VariableDescriptorWithAccessors
|
||||
?: return emptyList()
|
||||
|
||||
if (propertyDescriptor is LocalVariableDescriptor
|
||||
&& !element.languageVersionSettings.supportsFeature(LanguageFeature.LocalDelegatedProperties)) {
|
||||
return emptyList()
|
||||
}
|
||||
|
||||
val propertyReceiver = propertyDescriptor.extensionReceiverParameter ?: propertyDescriptor.dispatchReceiverParameter
|
||||
val propertyType = propertyDescriptor.type
|
||||
|
||||
val accessorReceiverType = TypeInfo(element, Variance.IN_VARIANCE)
|
||||
val builtIns = propertyDescriptor.builtIns
|
||||
val thisRefParam = ParameterInfo(TypeInfo(propertyReceiver?.type ?: builtIns.nullableNothingType, Variance.IN_VARIANCE))
|
||||
val kPropertyStarType = ReflectionTypes.createKPropertyStarType(propertyDescriptor.module) ?: return emptyList()
|
||||
val metadataParam = ParameterInfo(TypeInfo(kPropertyStarType, Variance.IN_VARIANCE), "property")
|
||||
|
||||
val callableInfos = SmartList<CallableInfo>()
|
||||
|
||||
if (isApplicableForAccessor(propertyDescriptor.getter)) {
|
||||
val getterInfo = FunctionInfo(
|
||||
name = OperatorNameConventions.GET_VALUE.asString(),
|
||||
receiverTypeInfo = accessorReceiverType,
|
||||
returnTypeInfo = TypeInfo(propertyType, Variance.OUT_VARIANCE),
|
||||
parameterInfos = listOf(thisRefParam, metadataParam),
|
||||
isOperator = true
|
||||
)
|
||||
callableInfos.add(getterInfo)
|
||||
}
|
||||
|
||||
if (propertyDescriptor.isVar && isApplicableForAccessor(propertyDescriptor.setter)) {
|
||||
val newValueParam = ParameterInfo(TypeInfo(propertyType, Variance.IN_VARIANCE))
|
||||
val setterInfo = FunctionInfo(
|
||||
name = OperatorNameConventions.SET_VALUE.asString(),
|
||||
receiverTypeInfo = accessorReceiverType,
|
||||
returnTypeInfo = TypeInfo(builtIns.unitType, Variance.OUT_VARIANCE),
|
||||
parameterInfos = listOf(thisRefParam, metadataParam, newValueParam),
|
||||
isOperator = true
|
||||
)
|
||||
callableInfos.add(setterInfo)
|
||||
}
|
||||
|
||||
return callableInfos
|
||||
}
|
||||
}
|
||||
-64
@@ -1,64 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.quickfix.createFromUsage.createCallable
|
||||
|
||||
import org.jetbrains.kotlin.diagnostics.Diagnostic
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.resolveToCall
|
||||
import org.jetbrains.kotlin.idea.core.quickfix.QuickFixUtil
|
||||
import org.jetbrains.kotlin.idea.project.builtIns
|
||||
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.CallableInfo
|
||||
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.FunctionInfo
|
||||
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.ParameterInfo
|
||||
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.TypeInfo
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.types.ErrorUtils
|
||||
import org.jetbrains.kotlin.types.Variance
|
||||
import org.jetbrains.kotlin.types.expressions.OperatorConventions
|
||||
import org.jetbrains.kotlin.util.OperatorNameConventions
|
||||
import java.util.*
|
||||
|
||||
object CreateSetFunctionActionFactory : CreateGetSetFunctionActionFactory(isGet = false) {
|
||||
override fun createCallableInfo(element: KtArrayAccessExpression, diagnostic: Diagnostic): CallableInfo? {
|
||||
val arrayExpr = element.arrayExpression ?: return null
|
||||
val arrayType = TypeInfo(arrayExpr, Variance.IN_VARIANCE)
|
||||
|
||||
val builtIns = element.builtIns
|
||||
|
||||
val parameters = element.indexExpressions.mapTo(ArrayList<ParameterInfo>()) {
|
||||
ParameterInfo(TypeInfo(it, Variance.IN_VARIANCE))
|
||||
}
|
||||
val assignmentExpr = QuickFixUtil.getParentElementOfType(diagnostic, KtOperationExpression::class.java) ?: return null
|
||||
val valType = when (assignmentExpr) {
|
||||
is KtBinaryExpression -> {
|
||||
TypeInfo(assignmentExpr.right ?: return null, Variance.IN_VARIANCE)
|
||||
}
|
||||
is KtUnaryExpression -> {
|
||||
if (assignmentExpr.operationToken !in OperatorConventions.INCREMENT_OPERATIONS) return null
|
||||
|
||||
val rhsType = assignmentExpr.resolveToCall()?.resultingDescriptor?.returnType
|
||||
TypeInfo(if (rhsType == null || ErrorUtils.containsErrorType(rhsType)) builtIns.anyType else rhsType, Variance.IN_VARIANCE)
|
||||
}
|
||||
else -> return null
|
||||
}
|
||||
parameters.add(ParameterInfo(valType, "value"))
|
||||
|
||||
val returnType = TypeInfo(builtIns.unitType, Variance.OUT_VARIANCE)
|
||||
return FunctionInfo(
|
||||
OperatorNameConventions.SET.asString(), arrayType, returnType, Collections.emptyList(), parameters, isOperator = true
|
||||
)
|
||||
}
|
||||
}
|
||||
-44
@@ -1,44 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.quickfix.createFromUsage.createCallable
|
||||
|
||||
import org.jetbrains.kotlin.diagnostics.Diagnostic
|
||||
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.CallableInfo
|
||||
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.FunctionInfo
|
||||
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.TypeInfo
|
||||
import org.jetbrains.kotlin.lexer.KtToken
|
||||
import org.jetbrains.kotlin.psi.KtUnaryExpression
|
||||
import org.jetbrains.kotlin.types.Variance
|
||||
import org.jetbrains.kotlin.types.expressions.OperatorConventions
|
||||
|
||||
object CreateUnaryOperationActionFactory: CreateCallableMemberFromUsageFactory<KtUnaryExpression>() {
|
||||
override fun getElementOfInterest(diagnostic: Diagnostic): KtUnaryExpression? {
|
||||
return diagnostic.psiElement.parent as? KtUnaryExpression
|
||||
}
|
||||
|
||||
override fun createCallableInfo(element: KtUnaryExpression, diagnostic: Diagnostic): CallableInfo? {
|
||||
val token = element.operationToken as KtToken
|
||||
val operationName = OperatorConventions.getNameForOperationSymbol(token, true, false) ?: return null
|
||||
val incDec = token in OperatorConventions.INCREMENT_OPERATIONS
|
||||
|
||||
val receiverExpr = element.baseExpression ?: return null
|
||||
|
||||
val receiverType = TypeInfo(receiverExpr, Variance.IN_VARIANCE)
|
||||
val returnType = if (incDec) TypeInfo.ByReceiverType(Variance.OUT_VARIANCE) else TypeInfo(element, Variance.OUT_VARIANCE)
|
||||
return FunctionInfo(operationName.asString(), receiverType, returnType, isOperator = true)
|
||||
}
|
||||
}
|
||||
-212
@@ -1,212 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations
|
||||
|
||||
import com.intellij.openapi.actionSystem.DataContext
|
||||
import com.intellij.openapi.actionSystem.LangDataKeys
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.psi.PsiDirectory
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.PsiPackage
|
||||
import com.intellij.psi.PsiReference
|
||||
import com.intellij.refactoring.JavaRefactoringSettings
|
||||
import com.intellij.refactoring.RefactoringBundle
|
||||
import com.intellij.refactoring.move.MoveCallback
|
||||
import com.intellij.refactoring.move.MoveHandlerDelegate
|
||||
import com.intellij.refactoring.move.moveClassesOrPackages.MoveClassesOrPackagesImpl
|
||||
import com.intellij.refactoring.move.moveFilesOrDirectories.MoveFilesOrDirectoriesUtil
|
||||
import com.intellij.refactoring.util.CommonRefactoringUtil
|
||||
import org.jetbrains.kotlin.asJava.unwrapped
|
||||
import org.jetbrains.kotlin.idea.core.getPackage
|
||||
import org.jetbrains.kotlin.idea.refactoring.canRefactor
|
||||
import org.jetbrains.kotlin.idea.refactoring.move.invokeMoveFilesOrDirectoriesRefactoring
|
||||
import org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations.ui.KotlinAwareMoveFilesOrDirectoriesDialog
|
||||
import org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations.ui.KotlinSelectNestedClassRefactoringDialog
|
||||
import org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations.ui.MoveKotlinNestedClassesDialog
|
||||
import org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations.ui.MoveKotlinTopLevelDeclarationsDialog
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject
|
||||
import org.jetbrains.kotlin.psi.psiUtil.getElementTextWithContext
|
||||
import org.jetbrains.kotlin.psi.psiUtil.isTopLevelInFileOrScript
|
||||
import java.util.*
|
||||
|
||||
class MoveKotlinDeclarationsHandler : MoveHandlerDelegate() {
|
||||
private fun getUniqueContainer(elements: Array<out PsiElement>): PsiElement? {
|
||||
val getContainer: (PsiElement) -> PsiElement? =
|
||||
if (elements.any { !isTopLevelInFileOrScript(it) }) { e ->
|
||||
when (e) {
|
||||
is KtNamedDeclaration -> e.containingClassOrObject ?: e.parent
|
||||
is KtFile -> e.parent
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
else { e ->
|
||||
e.containingFile?.parent
|
||||
}
|
||||
return elements.mapNotNullTo(LinkedHashSet(), getContainer).singleOrNull()
|
||||
}
|
||||
|
||||
private fun KtNamedDeclaration.canMove() = if (this is KtClassOrObject) !isLocal else parent is KtFile
|
||||
|
||||
private fun doMoveWithCheck(
|
||||
project: Project, elements: Array<out PsiElement>, targetContainer: PsiElement?, callback: MoveCallback?, editor: Editor?
|
||||
): Boolean {
|
||||
if (!CommonRefactoringUtil.checkReadOnlyStatusRecursively(project, elements.toList(), true)) return false
|
||||
|
||||
val container = getUniqueContainer(elements)
|
||||
if (container == null) {
|
||||
CommonRefactoringUtil.showErrorHint(
|
||||
project, editor, "All declarations must belong to the same directory or class", MOVE_DECLARATIONS, null
|
||||
)
|
||||
return false
|
||||
}
|
||||
|
||||
val elementsToSearch = elements.flatMapTo(LinkedHashSet<KtNamedDeclaration>()) {
|
||||
when (it) {
|
||||
is KtNamedDeclaration -> listOf(it)
|
||||
is KtFile -> it.declarations.filterIsInstance<KtNamedDeclaration>()
|
||||
else -> emptyList()
|
||||
}
|
||||
}
|
||||
|
||||
// todo: allow moving companion object
|
||||
if (elementsToSearch.any { it is KtObjectDeclaration && it.isCompanion() }) {
|
||||
val message = RefactoringBundle.getCannotRefactorMessage("Move declaration is not supported for companion objects")
|
||||
CommonRefactoringUtil.showErrorHint(project, editor, message, MOVE_DECLARATIONS, null)
|
||||
return true
|
||||
}
|
||||
|
||||
if (elementsToSearch.any { !it.canMove() }) {
|
||||
val message = RefactoringBundle.getCannotRefactorMessage("Move declaration is only supported for top-level declarations and nested classes")
|
||||
CommonRefactoringUtil.showErrorHint(project, editor, message, MOVE_DECLARATIONS, null)
|
||||
return true
|
||||
}
|
||||
|
||||
if (elementsToSearch.any { it is KtEnumEntry }) {
|
||||
val message = RefactoringBundle.getCannotRefactorMessage("Move declaration is not supported for enum entries")
|
||||
CommonRefactoringUtil.showErrorHint(project, editor, message, MOVE_DECLARATIONS, null)
|
||||
return true
|
||||
}
|
||||
|
||||
if (elements.all { it is KtFile }) {
|
||||
val initialTargetElement = when {
|
||||
targetContainer is PsiPackage || targetContainer is PsiDirectory -> targetContainer
|
||||
container is PsiPackage || container is PsiDirectory -> container
|
||||
else -> null
|
||||
}
|
||||
val initialTargetDirectory = MoveFilesOrDirectoriesUtil.resolveToDirectory(project, initialTargetElement)
|
||||
val dialog = KotlinAwareMoveFilesOrDirectoriesDialog(project) {
|
||||
invokeMoveFilesOrDirectoriesRefactoring(it, project, elements, initialTargetDirectory, callback)
|
||||
}
|
||||
dialog.setData(elements, initialTargetDirectory, "refactoring.moveFile")
|
||||
dialog.show()
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
when (container) {
|
||||
is PsiDirectory, is PsiPackage, is KtFile -> {
|
||||
val targetPackageName = MoveClassesOrPackagesImpl.getInitialTargetPackageName(targetContainer, elements)
|
||||
val targetDirectory = if (targetContainer != null) {
|
||||
MoveClassesOrPackagesImpl.getInitialTargetDirectory(targetContainer, elements)
|
||||
}
|
||||
else null
|
||||
val searchInComments = JavaRefactoringSettings.getInstance()!!.MOVE_SEARCH_IN_COMMENTS
|
||||
val searchInText = JavaRefactoringSettings.getInstance()!!.MOVE_SEARCH_FOR_TEXT
|
||||
val targetFile = targetContainer as? KtFile
|
||||
val moveToPackage = targetContainer !is KtFile
|
||||
|
||||
MoveKotlinTopLevelDeclarationsDialog(
|
||||
project, elementsToSearch, targetPackageName, targetDirectory, targetFile, moveToPackage, searchInComments, searchInText, callback
|
||||
).show()
|
||||
}
|
||||
|
||||
is KtClassOrObject -> {
|
||||
if (elementsToSearch.size > 1) {
|
||||
// todo: allow moving multiple classes to upper level
|
||||
if (targetContainer !is KtClassOrObject) {
|
||||
val message = RefactoringBundle.getCannotRefactorMessage("Moving multiple nested classes to top-level is not supported")
|
||||
CommonRefactoringUtil.showErrorHint(project, editor, message, MOVE_DECLARATIONS, null)
|
||||
return true
|
||||
}
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
MoveKotlinNestedClassesDialog(project,
|
||||
elementsToSearch.filterIsInstance<KtClassOrObject>(),
|
||||
container,
|
||||
targetContainer,
|
||||
callback).show()
|
||||
return true
|
||||
}
|
||||
KotlinSelectNestedClassRefactoringDialog.chooseNestedClassRefactoring(elementsToSearch.first() as KtClassOrObject,
|
||||
targetContainer)
|
||||
}
|
||||
|
||||
else -> throw AssertionError("Unexpected container: ${container.getElementTextWithContext()}")
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
private fun canMove(elements: Array<out PsiElement>, targetContainer: PsiElement?, editorMode: Boolean): Boolean {
|
||||
if (!super.canMove(elements, targetContainer)) return false
|
||||
val container = getUniqueContainer(elements) ?: return false
|
||||
|
||||
if (container is KtClassOrObject && targetContainer != null && targetContainer !is KtClassOrObject && elements.size > 1) {
|
||||
return false
|
||||
}
|
||||
|
||||
return elements.all { e ->
|
||||
when {
|
||||
e is KtClass || e is KtObjectDeclaration && !e.isObjectLiteral() || e is KtNamedFunction || e is KtProperty || e is KtTypeAlias ->
|
||||
(editorMode || (e as KtNamedDeclaration).canMove()) && e.canRefactor()
|
||||
e is KtFile ->
|
||||
e.declarations.any { it is KtNamedDeclaration } && e.canRefactor()
|
||||
else ->
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun canMove(elements: Array<out PsiElement>, targetContainer: PsiElement?): Boolean {
|
||||
return canMove(elements, targetContainer, false)
|
||||
}
|
||||
|
||||
override fun isValidTarget(psiElement: PsiElement?, sources: Array<out PsiElement>): Boolean {
|
||||
return psiElement is PsiPackage
|
||||
|| (psiElement is PsiDirectory && psiElement.getPackage() != null)
|
||||
|| psiElement is KtFile
|
||||
|| (psiElement is KtClassOrObject
|
||||
&& !(psiElement.hasModifier(KtTokens.ANNOTATION_KEYWORD))
|
||||
&& !sources.any { it.parent is KtFile })
|
||||
}
|
||||
|
||||
override fun doMove(project: Project, elements: Array<out PsiElement>, targetContainer: PsiElement?, callback: MoveCallback?) {
|
||||
doMoveWithCheck(project, elements, targetContainer, callback, null)
|
||||
}
|
||||
|
||||
override fun tryToMove(
|
||||
element: PsiElement, project: Project, dataContext: DataContext?, reference: PsiReference?, editor: Editor?
|
||||
): Boolean {
|
||||
val elementsToMove = element.unwrapped?.let { arrayOf(it) } ?: PsiElement.EMPTY_ARRAY
|
||||
val targetContainer = dataContext?.let { dataContext -> LangDataKeys.TARGET_PSI_ELEMENT.getData(dataContext) }
|
||||
return canMove(elementsToMove, targetContainer, true) && doMoveWithCheck(project, elementsToMove, targetContainer, null, editor)
|
||||
}
|
||||
}
|
||||
|
||||
private val MOVE_DECLARATIONS = "Move Declarations"
|
||||
-39
@@ -1,39 +0,0 @@
|
||||
/*
|
||||
* Copyright 2000-2018 JetBrains s.r.o. 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.rename
|
||||
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.PsiMethod
|
||||
import com.intellij.psi.PsiReference
|
||||
import com.intellij.psi.PsiType
|
||||
import com.intellij.refactoring.rename.RenameJavaMethodProcessor
|
||||
import org.jetbrains.kotlin.asJava.elements.KtLightMethod
|
||||
import org.jetbrains.kotlin.idea.references.SyntheticPropertyAccessorReference
|
||||
import org.jetbrains.kotlin.load.java.JvmAbi
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.synthetic.SyntheticJavaPropertyDescriptor
|
||||
import org.jetbrains.kotlin.utils.ifEmpty
|
||||
|
||||
class KotlinAwareJavaGetterRenameProcessor : RenameJavaMethodProcessor() {
|
||||
override fun canProcessElement(element: PsiElement) = element is PsiMethod && element !is KtLightMethod && JvmAbi.isGetterName(element.name)
|
||||
|
||||
override fun findReferences(element: PsiElement): MutableCollection<PsiReference> {
|
||||
val getterReferences = super.findReferences(element)
|
||||
val getter = element as? PsiMethod ?: return getterReferences
|
||||
val propertyName = SyntheticJavaPropertyDescriptor.propertyNameByGetMethodName(Name.identifier(getter.name)) ?: return getterReferences
|
||||
val setterName = JvmAbi.setterName(propertyName.asString())
|
||||
val containingClass = getter.containingClass ?: return getterReferences
|
||||
val setterReferences = containingClass
|
||||
.findMethodsByName(setterName, true)
|
||||
.filter { it.parameterList.parameters.size == 1 && it.returnType == PsiType.VOID }
|
||||
.flatMap { super.findReferences(it).filterIsInstance<SyntheticPropertyAccessorReference.Setter>() }
|
||||
.ifEmpty { return getterReferences }
|
||||
return ArrayList<PsiReference>(getterReferences.size + setterReferences.size).apply {
|
||||
addAll(getterReferences)
|
||||
setterReferences.mapTo(this) { SyntheticPropertyAccessorReference.Getter(it.expression) }
|
||||
}
|
||||
}
|
||||
}
|
||||
-486
@@ -1,486 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.refactoring.safeDelete
|
||||
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.module.Module
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.ui.Messages
|
||||
import com.intellij.openapi.util.Condition
|
||||
import com.intellij.openapi.util.Conditions
|
||||
import com.intellij.openapi.util.Key
|
||||
import com.intellij.psi.*
|
||||
import com.intellij.psi.search.searches.ReferencesSearch
|
||||
import com.intellij.refactoring.RefactoringBundle
|
||||
import com.intellij.refactoring.safeDelete.JavaSafeDeleteProcessor
|
||||
import com.intellij.refactoring.safeDelete.NonCodeUsageSearchInfo
|
||||
import com.intellij.refactoring.safeDelete.usageInfo.SafeDeleteOverrideAnnotation
|
||||
import com.intellij.refactoring.safeDelete.usageInfo.SafeDeleteOverridingMethodUsageInfo
|
||||
import com.intellij.refactoring.safeDelete.usageInfo.SafeDeleteReferenceJavaDeleteUsageInfo
|
||||
import com.intellij.refactoring.safeDelete.usageInfo.SafeDeleteReferenceSimpleDeleteUsageInfo
|
||||
import com.intellij.usageView.UsageInfo
|
||||
import org.jetbrains.annotations.TestOnly
|
||||
import org.jetbrains.kotlin.asJava.*
|
||||
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.Modality
|
||||
import org.jetbrains.kotlin.idea.KotlinBundle
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.resolveToCall
|
||||
import org.jetbrains.kotlin.idea.core.deleteElementAndCleanParent
|
||||
import org.jetbrains.kotlin.idea.refactoring.checkSuperMethods
|
||||
import org.jetbrains.kotlin.idea.refactoring.formatClass
|
||||
import org.jetbrains.kotlin.idea.refactoring.formatFunction
|
||||
import org.jetbrains.kotlin.idea.refactoring.withExpectedActuals
|
||||
import org.jetbrains.kotlin.idea.refactoring.isTrueJavaMethod
|
||||
import org.jetbrains.kotlin.idea.references.KtReference
|
||||
import org.jetbrains.kotlin.idea.search.ideaExtensions.KotlinReferencesSearchOptions
|
||||
import org.jetbrains.kotlin.idea.search.ideaExtensions.KotlinReferencesSearchParameters
|
||||
import org.jetbrains.kotlin.idea.search.projectScope
|
||||
import org.jetbrains.kotlin.idea.search.usagesSearch.processDelegationCallConstructorUsages
|
||||
import org.jetbrains.kotlin.idea.util.isExpectDeclaration
|
||||
import org.jetbrains.kotlin.idea.util.liftToExpected
|
||||
import org.jetbrains.kotlin.idea.util.runOnExpectAndAllActuals
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType
|
||||
import org.jetbrains.kotlin.psi.psiUtil.getParentOfTypeAndBranch
|
||||
import org.jetbrains.kotlin.psi.psiUtil.hasActualModifier
|
||||
import org.jetbrains.kotlin.psi.psiUtil.parameterIndex
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
|
||||
import org.jetbrains.kotlin.utils.SmartSet
|
||||
import org.jetbrains.kotlin.utils.ifEmpty
|
||||
import java.util.*
|
||||
|
||||
class KotlinSafeDeleteProcessor : JavaSafeDeleteProcessor() {
|
||||
companion object {
|
||||
@set:TestOnly
|
||||
internal var Project.ALLOW_LIFTING_ACTUAL_PARAMETER_TO_EXPECTED
|
||||
by NotNullableUserDataProperty(Key.create("ALLOW_LIFTING_ACTUAL_PARAMETER_TO_EXPECTED"), true)
|
||||
|
||||
private var KtDeclaration.dropActualModifier: Boolean? by UserDataProperty(Key.create("DROP_ACTUAL_MODIFIER"))
|
||||
}
|
||||
|
||||
override fun handlesElement(element: PsiElement): Boolean = element.canDeleteElement()
|
||||
|
||||
override fun findUsages(
|
||||
element: PsiElement, allElementsToDelete: Array<out PsiElement>, usages: MutableList<UsageInfo>
|
||||
): NonCodeUsageSearchInfo {
|
||||
val deleteSet = SmartSet.create<PsiElement>()
|
||||
deleteSet.addAll(allElementsToDelete)
|
||||
|
||||
fun getIgnoranceCondition() = Condition<PsiElement> {
|
||||
if (it is KtFile) return@Condition false
|
||||
deleteSet.any { element -> JavaSafeDeleteProcessor.isInside(it, element.unwrapped) }
|
||||
}
|
||||
|
||||
fun getSearchInfo(element: PsiElement) = NonCodeUsageSearchInfo(getIgnoranceCondition(), element)
|
||||
|
||||
fun searchKotlinDeclarationReferences(declaration: KtDeclaration): Sequence<PsiReference> {
|
||||
val elementsToSearch = if (declaration is KtParameter) declaration.withExpectedActuals() else listOf(declaration)
|
||||
return elementsToSearch.asSequence().flatMap {
|
||||
val searchParameters = KotlinReferencesSearchParameters(
|
||||
it,
|
||||
if (it.hasActualModifier()) it.project.projectScope() else it.useScope,
|
||||
kotlinOptions = KotlinReferencesSearchOptions(acceptCallableOverrides = true)
|
||||
)
|
||||
ReferencesSearch.search(searchParameters)
|
||||
.asSequence()
|
||||
.filterNot { reference -> getIgnoranceCondition().value(reference.element) }
|
||||
}
|
||||
}
|
||||
|
||||
fun findKotlinParameterUsages(parameter: KtParameter) {
|
||||
val ownerFunction = parameter.ownerFunction as? KtFunction ?: return
|
||||
val index = parameter.parameterIndex()
|
||||
for (reference in searchKotlinDeclarationReferences(ownerFunction)) {
|
||||
val callee = reference.element as? KtExpression ?: continue
|
||||
val resolvedCall = callee.resolveToCall(BodyResolveMode.FULL) ?: continue
|
||||
val parameterDescriptor = resolvedCall.candidateDescriptor.valueParameters.getOrNull(index) ?: continue
|
||||
val resolvedArgument = resolvedCall.valueArguments[parameterDescriptor] ?: continue
|
||||
val arguments = resolvedArgument.arguments.filterIsInstance<KtValueArgument>()
|
||||
if (arguments.isEmpty()) continue
|
||||
|
||||
usages.add(SafeDeleteValueArgumentListUsageInfo(parameter, *arguments.toTypedArray()))
|
||||
}
|
||||
}
|
||||
|
||||
fun findKotlinDeclarationUsages(declaration: KtDeclaration): NonCodeUsageSearchInfo {
|
||||
searchKotlinDeclarationReferences(declaration).mapNotNullTo(usages) { reference ->
|
||||
val refElement = reference.element ?: return@mapNotNullTo null
|
||||
refElement.getNonStrictParentOfType<KtImportDirective>()?.let { importDirective ->
|
||||
SafeDeleteImportDirectiveUsageInfo(importDirective, element)
|
||||
} ?: SafeDeleteReferenceSimpleDeleteUsageInfo(refElement, declaration, false)
|
||||
}
|
||||
|
||||
if (declaration is KtParameter) {
|
||||
findKotlinParameterUsages(declaration)
|
||||
}
|
||||
|
||||
return getSearchInfo(declaration)
|
||||
}
|
||||
|
||||
fun asLightElements(ktElements: Array<out PsiElement>) =
|
||||
ktElements.flatMap { (it as? KtElement)?.toLightElements() ?: listOf(it) }.toTypedArray()
|
||||
|
||||
fun findUsagesByJavaProcessor(element: PsiElement, forceReferencedElementUnwrapping: Boolean): NonCodeUsageSearchInfo? {
|
||||
val javaUsages = ArrayList<UsageInfo>()
|
||||
val searchInfo = super.findUsages(element, asLightElements(allElementsToDelete), javaUsages)
|
||||
|
||||
javaUsages.filterIsInstance<SafeDeleteOverridingMethodUsageInfo>().mapNotNullTo(deleteSet) { it.element }
|
||||
|
||||
val ignoranceCondition = getIgnoranceCondition()
|
||||
|
||||
javaUsages.mapNotNullTo(usages) { usageInfo ->
|
||||
when (usageInfo) {
|
||||
is SafeDeleteOverridingMethodUsageInfo ->
|
||||
usageInfo.smartPointer.element?.let { usageElement ->
|
||||
KotlinSafeDeleteOverridingUsageInfo(usageElement, usageInfo.referencedElement)
|
||||
}
|
||||
|
||||
is SafeDeleteOverrideAnnotation ->
|
||||
usageInfo.smartPointer.element?.let { usageElement ->
|
||||
when {
|
||||
usageElement.isTrueJavaMethod() -> usageInfo
|
||||
usageElement.toLightMethods().all { method -> method.findSuperMethods().isEmpty() } -> {
|
||||
KotlinSafeDeleteOverrideAnnotation(usageElement, usageInfo.referencedElement) as UsageInfo
|
||||
}
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
is SafeDeleteReferenceJavaDeleteUsageInfo ->
|
||||
usageInfo.element?.let { usageElement ->
|
||||
when {
|
||||
usageElement.getNonStrictParentOfType<KtValueArgumentName>() != null -> null
|
||||
ignoranceCondition.value(usageElement) -> null
|
||||
else -> {
|
||||
usageElement.getNonStrictParentOfType<KtImportDirective>()?.let { importDirective ->
|
||||
SafeDeleteImportDirectiveUsageInfo(importDirective, element)
|
||||
} ?: usageElement.getParentOfTypeAndBranch<KtSuperTypeEntry> { typeReference }?.let {
|
||||
if (element is PsiClass && element.isInterface) SafeDeleteSuperTypeUsageInfo(it, element) else usageInfo
|
||||
} ?: if (forceReferencedElementUnwrapping) {
|
||||
SafeDeleteReferenceJavaDeleteUsageInfo(usageElement, element.unwrapped, usageInfo.isSafeDelete)
|
||||
} else usageInfo
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
else -> usageInfo
|
||||
}
|
||||
}
|
||||
|
||||
return searchInfo
|
||||
}
|
||||
|
||||
fun findUsagesByJavaProcessor(elements: Sequence<PsiElement>, insideDeleted: Condition<PsiElement>): Condition<PsiElement> =
|
||||
elements
|
||||
.mapNotNull { element -> findUsagesByJavaProcessor(element, true)?.insideDeletedCondition }
|
||||
.fold(insideDeleted) { condition1, condition2 -> Conditions.or(condition1, condition2) }
|
||||
|
||||
fun findUsagesByJavaProcessor(ktDeclaration: KtDeclaration): NonCodeUsageSearchInfo {
|
||||
val lightElements = ktDeclaration.toLightElements()
|
||||
if (lightElements.isEmpty()) {
|
||||
return findKotlinDeclarationUsages(ktDeclaration)
|
||||
}
|
||||
return NonCodeUsageSearchInfo(
|
||||
findUsagesByJavaProcessor(
|
||||
lightElements.asSequence(),
|
||||
getIgnoranceCondition()
|
||||
),
|
||||
ktDeclaration
|
||||
)
|
||||
}
|
||||
|
||||
fun findTypeParameterUsages(parameter: KtTypeParameter) {
|
||||
val owner = parameter.getNonStrictParentOfType<KtTypeParameterListOwner>() ?: return
|
||||
|
||||
val parameterList = owner.typeParameters
|
||||
val parameterIndex = parameterList.indexOf(parameter)
|
||||
|
||||
for (reference in ReferencesSearch.search(owner)) {
|
||||
if (reference !is KtReference) continue
|
||||
|
||||
val referencedElement = reference.element
|
||||
|
||||
val argList = referencedElement.getNonStrictParentOfType<KtUserType>()?.typeArgumentList
|
||||
?: referencedElement.getNonStrictParentOfType<KtCallExpression>()?.typeArgumentList
|
||||
|
||||
if (argList != null) {
|
||||
val projections = argList.arguments
|
||||
if (parameterIndex < projections.size) {
|
||||
usages.add(SafeDeleteTypeArgumentListUsageInfo(projections[parameterIndex], parameter))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun findDelegationCallUsages(element: PsiElement) {
|
||||
val constructors = when (element) {
|
||||
is PsiClass -> element.constructors
|
||||
is PsiMethod -> arrayOf(element)
|
||||
else -> return
|
||||
}
|
||||
for (constructor in constructors) {
|
||||
constructor.processDelegationCallConstructorUsages(constructor.useScope) {
|
||||
if (!getIgnoranceCondition().value(it)) {
|
||||
usages.add(SafeDeleteReferenceSimpleDeleteUsageInfo(it, element, false))
|
||||
}
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return when (element) {
|
||||
is KtClassOrObject -> {
|
||||
element.toLightClass()?.let { klass ->
|
||||
findDelegationCallUsages(klass)
|
||||
findUsagesByJavaProcessor(klass, false)
|
||||
} ?: findKotlinDeclarationUsages(element)
|
||||
}
|
||||
|
||||
is KtSecondaryConstructor -> {
|
||||
if (element.hasActualModifier()) {
|
||||
findKotlinDeclarationUsages(element)
|
||||
} else {
|
||||
element.getRepresentativeLightMethod()?.let { method ->
|
||||
findDelegationCallUsages(method)
|
||||
findUsagesByJavaProcessor(method, false)
|
||||
} ?: findKotlinDeclarationUsages(element)
|
||||
}
|
||||
}
|
||||
|
||||
is KtNamedFunction -> {
|
||||
if (element.isLocal || element.hasActualModifier()) {
|
||||
findKotlinDeclarationUsages(element)
|
||||
}
|
||||
else {
|
||||
val lightMethods = element.toLightMethods()
|
||||
if (lightMethods.isNotEmpty()) {
|
||||
lightMethods.map { method -> findUsagesByJavaProcessor(method, false) }.firstOrNull()
|
||||
}
|
||||
else {
|
||||
findKotlinDeclarationUsages(element)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
is PsiMethod -> {
|
||||
findUsagesByJavaProcessor(element, false)
|
||||
}
|
||||
|
||||
is PsiClass -> {
|
||||
findUsagesByJavaProcessor(element, false)
|
||||
}
|
||||
|
||||
is KtProperty -> {
|
||||
if (element.isLocal || element.hasActualModifier()) {
|
||||
findKotlinDeclarationUsages(element)
|
||||
}
|
||||
else {
|
||||
findUsagesByJavaProcessor(element)
|
||||
}
|
||||
}
|
||||
|
||||
is KtTypeParameter -> {
|
||||
findTypeParameterUsages(element)
|
||||
findUsagesByJavaProcessor(element)
|
||||
}
|
||||
|
||||
is KtParameter ->
|
||||
findUsagesByJavaProcessor(element)
|
||||
|
||||
is KtTypeAlias -> {
|
||||
findKotlinDeclarationUsages(element)
|
||||
}
|
||||
|
||||
else -> null
|
||||
} ?: getSearchInfo(element)
|
||||
}
|
||||
|
||||
override fun findConflicts(element: PsiElement, allElementsToDelete: Array<out PsiElement>): MutableCollection<String>? {
|
||||
if (element is KtNamedFunction || element is KtProperty) {
|
||||
val jetClass = element.getNonStrictParentOfType<KtClass>()
|
||||
if (jetClass == null || jetClass.getBody() != element.parent) return null
|
||||
|
||||
val modifierList = jetClass.modifierList
|
||||
if (modifierList != null && modifierList.hasModifier(KtTokens.ABSTRACT_KEYWORD)) return null
|
||||
|
||||
val bindingContext = (element as KtElement).analyze()
|
||||
|
||||
val declarationDescriptor =
|
||||
bindingContext[BindingContext.DECLARATION_TO_DESCRIPTOR, element] as? CallableMemberDescriptor ?: return null
|
||||
|
||||
return declarationDescriptor.overriddenDescriptors
|
||||
.asSequence()
|
||||
.filter { overridenDescriptor -> overridenDescriptor.modality == Modality.ABSTRACT }
|
||||
.mapTo(ArrayList()) { overridenDescriptor ->
|
||||
KotlinBundle.message(
|
||||
"x.implements.y",
|
||||
formatFunction(declarationDescriptor, true),
|
||||
formatClass(declarationDescriptor.containingDeclaration, true),
|
||||
formatFunction(overridenDescriptor, true),
|
||||
formatClass(overridenDescriptor.containingDeclaration, true)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
return super.findConflicts(element, allElementsToDelete)
|
||||
}
|
||||
|
||||
/*
|
||||
* Mostly copied from JavaSafeDeleteProcessor.preprocessUsages
|
||||
* Revision: d4fc033
|
||||
* (replaced original dialog)
|
||||
*/
|
||||
override fun preprocessUsages(project: Project, usages: Array<out UsageInfo>): Array<UsageInfo>? {
|
||||
val result = ArrayList<UsageInfo>()
|
||||
val overridingMethodUsages = ArrayList<UsageInfo>()
|
||||
|
||||
for (usage in usages) {
|
||||
if (usage is KotlinSafeDeleteOverridingUsageInfo) {
|
||||
overridingMethodUsages.add(usage)
|
||||
} else {
|
||||
result.add(usage)
|
||||
}
|
||||
}
|
||||
|
||||
if (!overridingMethodUsages.isEmpty()) {
|
||||
if (ApplicationManager.getApplication()!!.isUnitTestMode) {
|
||||
result.addAll(overridingMethodUsages)
|
||||
} else {
|
||||
val dialog = KotlinOverridingDialog(project, overridingMethodUsages)
|
||||
dialog.show()
|
||||
|
||||
if (!dialog.isOK) return null
|
||||
|
||||
result.addAll(dialog.selected)
|
||||
}
|
||||
}
|
||||
|
||||
return result.toTypedArray()
|
||||
}
|
||||
|
||||
private fun KtDeclaration.removeOrClean() {
|
||||
when (this) {
|
||||
is KtParameter -> {
|
||||
(parent as? KtParameterList)?.removeParameter(this)
|
||||
}
|
||||
is KtCallableDeclaration, is KtClassOrObject, is KtTypeAlias -> {
|
||||
delete()
|
||||
}
|
||||
else -> {
|
||||
removeModifier(KtTokens.IMPL_KEYWORD)
|
||||
removeModifier(KtTokens.ACTUAL_KEYWORD)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun prepareForDeletion(element: PsiElement) {
|
||||
if (element is KtDeclaration) {
|
||||
element.runOnExpectAndAllActuals(checkExpect = false) { it.removeOrClean() }
|
||||
}
|
||||
|
||||
when (element) {
|
||||
is PsiMethod -> element.cleanUpOverrides()
|
||||
|
||||
is KtNamedFunction ->
|
||||
if (!element.isLocal) {
|
||||
element.getRepresentativeLightMethod()?.cleanUpOverrides()
|
||||
}
|
||||
|
||||
is KtProperty ->
|
||||
if (!element.isLocal) {
|
||||
element.toLightMethods().forEach(PsiMethod::cleanUpOverrides)
|
||||
}
|
||||
|
||||
is KtTypeParameter ->
|
||||
element.deleteElementAndCleanParent()
|
||||
|
||||
is KtParameter -> {
|
||||
element.ownerFunction?.let {
|
||||
if (it.dropActualModifier == true) {
|
||||
it.removeModifier(KtTokens.IMPL_KEYWORD)
|
||||
it.removeModifier(KtTokens.ACTUAL_KEYWORD)
|
||||
it.dropActualModifier = null
|
||||
}
|
||||
}
|
||||
(element.parent as KtParameterList).removeParameter(element)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun shouldAllowPropagationToExpected(parameter: KtParameter): Boolean {
|
||||
if (ApplicationManager.getApplication().isUnitTestMode) return parameter.project.ALLOW_LIFTING_ACTUAL_PARAMETER_TO_EXPECTED
|
||||
|
||||
return Messages.showYesNoDialog(
|
||||
"Do you want to delete this parameter in expected declaration and all related actual ones?",
|
||||
RefactoringBundle.message("safe.delete.title"),
|
||||
Messages.getQuestionIcon()
|
||||
) == Messages.YES
|
||||
}
|
||||
|
||||
private fun shouldAllowPropagationToExpected(): Boolean {
|
||||
if (ApplicationManager.getApplication().isUnitTestMode) return true
|
||||
|
||||
return Messages.showYesNoDialog(
|
||||
"Do you want to delete expected declaration together with all related actual ones?",
|
||||
RefactoringBundle.message("safe.delete.title"),
|
||||
Messages.getQuestionIcon()
|
||||
) == Messages.YES
|
||||
}
|
||||
|
||||
override fun getElementsToSearch(
|
||||
element: PsiElement, module: Module?, allElementsToDelete: Collection<PsiElement>
|
||||
): Collection<PsiElement>? {
|
||||
when (element) {
|
||||
is KtParameter -> {
|
||||
val expectParameter = element.liftToExpected()
|
||||
if (expectParameter != null && expectParameter != element) {
|
||||
if (shouldAllowPropagationToExpected(element)) {
|
||||
return listOf(expectParameter)
|
||||
} else {
|
||||
element.ownerFunction?.dropActualModifier = true
|
||||
return listOf(element)
|
||||
}
|
||||
}
|
||||
|
||||
return element.toPsiParameters().flatMap { psiParameter ->
|
||||
checkParametersInMethodHierarchy(psiParameter) ?: emptyList()
|
||||
}.ifEmpty { listOf(element) }
|
||||
}
|
||||
|
||||
is KtDeclaration -> {
|
||||
if (element.hasActualModifier() || element.isExpectDeclaration()) {
|
||||
if (!shouldAllowPropagationToExpected()) {
|
||||
return null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
is PsiParameter ->
|
||||
return checkParametersInMethodHierarchy(element)
|
||||
}
|
||||
|
||||
if (ApplicationManager.getApplication()!!.isUnitTestMode) return Collections.singletonList(element)
|
||||
|
||||
return when (element) {
|
||||
is KtNamedFunction, is KtProperty -> checkSuperMethods(element as KtDeclaration, allElementsToDelete, "delete (with usage search)")
|
||||
else -> super.getElementsToSearch(element, module, allElementsToDelete)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,68 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.slicer
|
||||
|
||||
import com.intellij.ide.util.treeView.AbstractTreeStructure
|
||||
import com.intellij.openapi.actionSystem.DefaultActionGroup
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.slicer.*
|
||||
import org.jetbrains.kotlin.idea.references.mainReference
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.isPlainWithEscapes
|
||||
import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf
|
||||
|
||||
class KotlinSliceProvider : SliceLanguageSupportProvider {
|
||||
override fun createRootUsage(element: PsiElement, params: SliceAnalysisParams) = KotlinSliceUsage(element, params)
|
||||
|
||||
override fun getExpressionAtCaret(atCaret: PsiElement?, dataFlowToThis: Boolean): KtExpression? {
|
||||
val element =
|
||||
atCaret?.parentsWithSelf
|
||||
?.firstOrNull {
|
||||
it is KtProperty ||
|
||||
it is KtParameter ||
|
||||
it is KtDeclarationWithBody ||
|
||||
(it is KtClass && !it.hasExplicitPrimaryConstructor()) ||
|
||||
(it is KtExpression && it !is KtDeclaration)
|
||||
}
|
||||
?.let { KtPsiUtil.safeDeparenthesize(it as KtExpression) } ?: return null
|
||||
if (dataFlowToThis) {
|
||||
if (element is KtConstantExpression) return null
|
||||
if (element is KtStringTemplateExpression && element.isPlainWithEscapes()) return null
|
||||
if (element is KtClassLiteralExpression) return null
|
||||
if (element is KtCallableReferenceExpression) return null
|
||||
}
|
||||
return element
|
||||
}
|
||||
|
||||
override fun getElementForDescription(element: PsiElement): PsiElement {
|
||||
return (element as? KtSimpleNameExpression)?.mainReference?.resolve() ?: element
|
||||
}
|
||||
|
||||
override fun getRenderer() = KotlinSliceUsageCellRenderer
|
||||
|
||||
override fun startAnalyzeLeafValues(structure: AbstractTreeStructure, finalRunnable: Runnable) {
|
||||
|
||||
}
|
||||
|
||||
override fun startAnalyzeNullness(structure: AbstractTreeStructure, finalRunnable: Runnable) {
|
||||
|
||||
}
|
||||
|
||||
override fun registerExtraPanelActions(group: DefaultActionGroup, builder: SliceTreeBuilder) {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.slicer
|
||||
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.slicer.SliceAnalysisParams
|
||||
import com.intellij.slicer.SliceUsage
|
||||
import com.intellij.util.Processor
|
||||
import org.jetbrains.kotlin.psi.KtExpression
|
||||
|
||||
open class KotlinSliceUsage : SliceUsage {
|
||||
val lambdaLevel: Int
|
||||
val forcedExpressionMode: Boolean
|
||||
|
||||
constructor(element: PsiElement, parent: SliceUsage, lambdaLevel: Int, forcedExpressionMode: Boolean) : super(element, parent) {
|
||||
this.lambdaLevel = lambdaLevel
|
||||
this.forcedExpressionMode = forcedExpressionMode
|
||||
}
|
||||
|
||||
constructor(element: PsiElement, params: SliceAnalysisParams) : super(element, params) {
|
||||
this.lambdaLevel = 0
|
||||
this.forcedExpressionMode = false
|
||||
}
|
||||
|
||||
override fun copy(): KotlinSliceUsage {
|
||||
val element = usageInfo.element!!
|
||||
if (parent == null) return KotlinSliceUsage(element, params)
|
||||
return KotlinSliceUsage(element, parent, lambdaLevel, forcedExpressionMode)
|
||||
}
|
||||
|
||||
public override fun processUsagesFlownDownTo(element: PsiElement, uniqueProcessor: Processor<SliceUsage>) {
|
||||
InflowSlicer(element as? KtExpression ?: return, uniqueProcessor, this).processChildren()
|
||||
}
|
||||
|
||||
public override fun processUsagesFlownFromThe(element: PsiElement, uniqueProcessor: Processor<SliceUsage>) {
|
||||
OutflowSlicer(element as? KtExpression ?: return, uniqueProcessor, this).processChildren()
|
||||
}
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
/*
|
||||
* Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
|
||||
* that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
@file:Suppress("IncompatibleAPI")
|
||||
|
||||
package org.jetbrains.kotlin.idea.util.compat.statistic
|
||||
|
||||
/**
|
||||
* Should be dropped after abandoning 172.
|
||||
* BUNCH: 173
|
||||
*/
|
||||
typealias AbstractProjectsUsagesCollector = com.intellij.internal.statistic.AbstractApplicationUsagesCollector
|
||||
|
||||
/**
|
||||
* Should be dropped after abandoning 172.
|
||||
* BUNCH: 173
|
||||
*/
|
||||
fun getEnumUsage(key: String, value: Enum<*>?) = com.intellij.internal.statistic.getEnumUsage(key, value)
|
||||
|
||||
/**
|
||||
* Should be dropped after abandoning 172.
|
||||
* BUNCH: 173
|
||||
*/
|
||||
fun getBooleanUsage(key: String, value: Boolean) = com.intellij.internal.statistic.getBooleanUsage(key, value)
|
||||
@@ -1,324 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2016 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.versions
|
||||
|
||||
import com.intellij.openapi.diagnostic.Logger
|
||||
import com.intellij.openapi.module.Module
|
||||
import com.intellij.openapi.module.ModuleManager
|
||||
import com.intellij.openapi.module.ModuleUtil
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.projectRoots.JavaSdkVersion
|
||||
import com.intellij.openapi.projectRoots.Sdk
|
||||
import com.intellij.openapi.roots.LibraryOrderEntry
|
||||
import com.intellij.openapi.roots.ModuleRootManager
|
||||
import com.intellij.openapi.roots.OrderRootType
|
||||
import com.intellij.openapi.roots.ProjectFileIndex
|
||||
import com.intellij.openapi.roots.libraries.Library
|
||||
import com.intellij.openapi.ui.Messages
|
||||
import com.intellij.openapi.vfs.JarFileSystem
|
||||
import com.intellij.openapi.vfs.VfsUtil
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import com.intellij.psi.JavaPsiFacade
|
||||
import com.intellij.psi.PsiClass
|
||||
import com.intellij.psi.search.GlobalSearchScope
|
||||
import com.intellij.util.containers.MultiMap
|
||||
import com.intellij.util.indexing.FileBasedIndex
|
||||
import com.intellij.util.indexing.ScalarIndexExtension
|
||||
import com.intellij.util.text.VersionComparatorUtil
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.config.JvmTarget
|
||||
import org.jetbrains.kotlin.config.KotlinCompilerVersion
|
||||
import org.jetbrains.kotlin.idea.framework.JavaRuntimeDetectionUtil
|
||||
import org.jetbrains.kotlin.idea.framework.isExternalLibrary
|
||||
import org.jetbrains.kotlin.idea.util.application.runReadAction
|
||||
import org.jetbrains.kotlin.idea.util.projectStructure.version
|
||||
import org.jetbrains.kotlin.idea.util.runWithAlternativeResolveEnabled
|
||||
import org.jetbrains.kotlin.idea.vfilefinder.KotlinJavaScriptMetaFileIndex
|
||||
import org.jetbrains.kotlin.idea.vfilefinder.hasSomethingInPackage
|
||||
import org.jetbrains.kotlin.metadata.deserialization.BinaryVersion
|
||||
import org.jetbrains.kotlin.metadata.jvm.deserialization.JvmMetadataVersion
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.utils.JsMetadataVersion
|
||||
import org.jetbrains.kotlin.utils.KotlinPaths
|
||||
import org.jetbrains.kotlin.utils.LibraryUtils
|
||||
import org.jetbrains.kotlin.utils.PathUtil
|
||||
import java.io.File
|
||||
import java.util.*
|
||||
|
||||
fun getLibraryRootsWithAbiIncompatibleKotlinClasses(module: Module): Collection<BinaryVersionedFile<JvmMetadataVersion>> {
|
||||
return getLibraryRootsWithAbiIncompatibleVersion(module, JvmMetadataVersion.INSTANCE, KotlinJvmMetadataVersionIndex)
|
||||
}
|
||||
|
||||
fun getLibraryRootsWithAbiIncompatibleForKotlinJs(module: Module): Collection<BinaryVersionedFile<JsMetadataVersion>> {
|
||||
return getLibraryRootsWithAbiIncompatibleVersion(module, JsMetadataVersion.INSTANCE, KotlinJsMetadataVersionIndex)
|
||||
}
|
||||
|
||||
fun findAllUsedLibraries(project: Project): MultiMap<Library, Module> {
|
||||
val libraries = MultiMap<Library, Module>()
|
||||
|
||||
for (module in ModuleManager.getInstance(project).modules) {
|
||||
val moduleRootManager = ModuleRootManager.getInstance(module)
|
||||
|
||||
for (entry in moduleRootManager.orderEntries.filterIsInstance<LibraryOrderEntry>()) {
|
||||
val library = entry.library ?: continue
|
||||
|
||||
libraries.putValue(library, module)
|
||||
}
|
||||
}
|
||||
|
||||
return libraries
|
||||
}
|
||||
|
||||
enum class LibraryJarDescriptor(
|
||||
val jarName: String,
|
||||
val orderRootType: OrderRootType,
|
||||
val shouldExist: Boolean,
|
||||
val getPath: (KotlinPaths) -> File = { paths -> File(paths.libPath, jarName) }
|
||||
) {
|
||||
RUNTIME_JAR(PathUtil.KOTLIN_JAVA_STDLIB_JAR, OrderRootType.CLASSES, true, KotlinPaths::getStdlibPath) {
|
||||
override fun findExistingJar(library: Library): VirtualFile? {
|
||||
if (isExternalLibrary(library)) return null
|
||||
return JavaRuntimeDetectionUtil.getRuntimeJar(Arrays.asList(*library.getFiles(OrderRootType.CLASSES)))
|
||||
}
|
||||
},
|
||||
|
||||
REFLECT_JAR(PathUtil.KOTLIN_JAVA_REFLECT_JAR, OrderRootType.CLASSES, false, KotlinPaths::getReflectPath),
|
||||
SCRIPT_RUNTIME_JAR(PathUtil.KOTLIN_JAVA_SCRIPT_RUNTIME_JAR, OrderRootType.CLASSES, true, KotlinPaths::getScriptRuntimePath),
|
||||
TEST_JAR(PathUtil.KOTLIN_TEST_JAR, OrderRootType.CLASSES, false, KotlinPaths::getKotlinTestPath),
|
||||
|
||||
@Deprecated("RUNTIME_JDK7_JAR should be used since 1.2")
|
||||
RUNTIME_JRE7_JAR(PathUtil.KOTLIN_JAVA_RUNTIME_JRE7_JAR, OrderRootType.CLASSES, false),
|
||||
RUNTIME_JDK7_JAR(PathUtil.KOTLIN_JAVA_RUNTIME_JDK7_JAR, OrderRootType.CLASSES, false),
|
||||
|
||||
@Deprecated("RUNTIME_JDK8_JAR should be used since 1.2")
|
||||
RUNTIME_JRE8_JAR(PathUtil.KOTLIN_JAVA_RUNTIME_JRE8_JAR, OrderRootType.CLASSES, false),
|
||||
RUNTIME_JDK8_JAR(PathUtil.KOTLIN_JAVA_RUNTIME_JDK8_JAR, OrderRootType.CLASSES, false),
|
||||
|
||||
@Deprecated("RUNTIME_JDK7_SOURCES_JAR should be used since 1.2")
|
||||
RUNTIME_JRE7_SOURCES_JAR(PathUtil.KOTLIN_JAVA_RUNTIME_JRE7_SRC_JAR, OrderRootType.SOURCES, false),
|
||||
RUNTIME_JDK7_SOURCES_JAR(PathUtil.KOTLIN_JAVA_RUNTIME_JDK7_SRC_JAR, OrderRootType.SOURCES, false),
|
||||
|
||||
@Deprecated("RUNTIME_JDK8_SOURCES_JAR should be used since 1.2")
|
||||
RUNTIME_JRE8_SOURCES_JAR(PathUtil.KOTLIN_JAVA_RUNTIME_JRE8_SRC_JAR, OrderRootType.SOURCES, false),
|
||||
RUNTIME_JDK8_SOURCES_JAR(PathUtil.KOTLIN_JAVA_RUNTIME_JDK8_SRC_JAR, OrderRootType.SOURCES, false),
|
||||
|
||||
RUNTIME_SRC_JAR(PathUtil.KOTLIN_JAVA_STDLIB_SRC_JAR, OrderRootType.SOURCES, false, KotlinPaths::getStdlibSourcesPath) {
|
||||
override fun findExistingJar(library: Library): VirtualFile? {
|
||||
return super.findExistingJar(library) ?: LibraryUtils.getJarFile(
|
||||
library.getFiles(orderRootType).toList(),
|
||||
PathUtil.KOTLIN_JAVA_STDLIB_SRC_JAR_OLD
|
||||
)
|
||||
}
|
||||
},
|
||||
REFLECT_SRC_JAR(PathUtil.KOTLIN_REFLECT_SRC_JAR, OrderRootType.SOURCES, false),
|
||||
TEST_SRC_JAR(PathUtil.KOTLIN_TEST_SRC_JAR, OrderRootType.SOURCES, false),
|
||||
|
||||
JS_STDLIB_JAR(PathUtil.JS_LIB_JAR_NAME, OrderRootType.CLASSES, true, KotlinPaths::getJsStdLibJarPath),
|
||||
JS_STDLIB_SRC_JAR(PathUtil.JS_LIB_SRC_JAR_NAME, OrderRootType.SOURCES, false, KotlinPaths::getJsStdLibSrcJarPath);
|
||||
|
||||
open fun findExistingJar(library: Library): VirtualFile? {
|
||||
if (isExternalLibrary(library)) return null
|
||||
return LibraryUtils.getJarFile(Arrays.asList(*library.getFiles(orderRootType)), jarName)
|
||||
}
|
||||
|
||||
fun getPathInPlugin() = getPath(PathUtil.kotlinPathsForIdeaPlugin)
|
||||
}
|
||||
|
||||
fun bundledRuntimeVersion(): String = KotlinCompilerVersion.VERSION
|
||||
|
||||
|
||||
fun getLocalJar(kotlinRuntimeJar: VirtualFile?): VirtualFile? {
|
||||
if (kotlinRuntimeJar == null) return null
|
||||
|
||||
val localJarFile = JarFileSystem.getInstance().getVirtualFileForJar(kotlinRuntimeJar)
|
||||
if (localJarFile != null) {
|
||||
return localJarFile
|
||||
}
|
||||
return kotlinRuntimeJar
|
||||
}
|
||||
|
||||
data class BinaryVersionedFile<out T : BinaryVersion>(val file: VirtualFile, val version: T, val supportedVersion: T)
|
||||
|
||||
private fun <T : BinaryVersion> getLibraryRootsWithAbiIncompatibleVersion(
|
||||
module: Module,
|
||||
supportedVersion: T,
|
||||
index: ScalarIndexExtension<T>
|
||||
): Collection<BinaryVersionedFile<T>> {
|
||||
val id = index.name
|
||||
|
||||
val moduleWithAllDependencies = setOf(module) + ModuleUtil.getAllDependentModules(module)
|
||||
val moduleWithAllDependentLibraries = GlobalSearchScope.union(
|
||||
moduleWithAllDependencies.map { it.moduleWithLibrariesScope }.toTypedArray()
|
||||
)
|
||||
|
||||
val allVersions = FileBasedIndex.getInstance().getAllKeys(id, module.project)
|
||||
val badVersions = allVersions.filterNot(BinaryVersion::isCompatible).toHashSet()
|
||||
val badRoots = hashSetOf<BinaryVersionedFile<T>>()
|
||||
val fileIndex = ProjectFileIndex.SERVICE.getInstance(module.project)
|
||||
|
||||
for (version in badVersions) {
|
||||
val indexedFiles = FileBasedIndex.getInstance().getContainingFiles(id, version, moduleWithAllDependentLibraries)
|
||||
for (indexedFile in indexedFiles) {
|
||||
val libraryRoot = fileIndex.getClassRootForFile(indexedFile) ?: error(
|
||||
"Only library roots were requested, and only class files should be indexed with the $id key. " +
|
||||
"File: ${indexedFile.path}"
|
||||
)
|
||||
badRoots.add(BinaryVersionedFile(com.intellij.util.PathUtil.getLocalFile(libraryRoot), version, supportedVersion))
|
||||
}
|
||||
}
|
||||
|
||||
return badRoots
|
||||
}
|
||||
|
||||
fun showRuntimeJarNotFoundDialog(project: Project, jarName: String) {
|
||||
Messages.showErrorDialog(
|
||||
project,
|
||||
jarName + " is not found. Make sure plugin is properly installed.",
|
||||
"No Runtime Found"
|
||||
)
|
||||
}
|
||||
|
||||
fun getKotlinJvmRuntimeMarkerClass(project: Project, scope: GlobalSearchScope): PsiClass? {
|
||||
return runReadAction {
|
||||
project.runWithAlternativeResolveEnabled {
|
||||
JavaPsiFacade.getInstance(project).findClass(KotlinBuiltIns.FQ_NAMES.unit.asString(), scope)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val KOTLIN_JS_FQ_NAME = FqName("kotlin.js")
|
||||
|
||||
fun hasKotlinJsKjsmFile(project: Project, scope: GlobalSearchScope): Boolean {
|
||||
return runReadAction {
|
||||
project.runWithAlternativeResolveEnabled {
|
||||
KotlinJavaScriptMetaFileIndex.hasSomethingInPackage(KOTLIN_JS_FQ_NAME, scope)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun getStdlibArtifactId(sdk: Sdk?, version: String): String {
|
||||
if (!hasJreSpecificRuntime(version)) {
|
||||
return MAVEN_STDLIB_ID
|
||||
}
|
||||
|
||||
val sdkVersion = sdk?.version
|
||||
if (hasJdkLikeUpdatedRuntime(version)) {
|
||||
return when {
|
||||
sdkVersion != null && sdkVersion.isAtLeast(JavaSdkVersion.JDK_1_8) -> MAVEN_STDLIB_ID_JDK8
|
||||
sdkVersion == JavaSdkVersion.JDK_1_7 -> MAVEN_STDLIB_ID_JDK7
|
||||
else -> MAVEN_STDLIB_ID
|
||||
}
|
||||
}
|
||||
|
||||
return when {
|
||||
sdkVersion != null && sdkVersion.isAtLeast(JavaSdkVersion.JDK_1_8) -> MAVEN_STDLIB_ID_JRE8
|
||||
sdkVersion == JavaSdkVersion.JDK_1_7 -> MAVEN_STDLIB_ID_JRE7
|
||||
else -> MAVEN_STDLIB_ID
|
||||
}
|
||||
}
|
||||
|
||||
fun getDefaultJvmTarget(sdk: Sdk?, version: String): JvmTarget? {
|
||||
if (!hasJreSpecificRuntime(version)) {
|
||||
return null
|
||||
}
|
||||
val sdkVersion = sdk?.version
|
||||
return when {
|
||||
sdkVersion == null -> null
|
||||
sdkVersion.isAtLeast(JavaSdkVersion.JDK_1_8) -> JvmTarget.JVM_1_8
|
||||
sdkVersion.isAtLeast(JavaSdkVersion.JDK_1_6) -> JvmTarget.JVM_1_6
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
fun isSnapshot(version: String): Boolean {
|
||||
return version.contains("SNAPSHOT", ignoreCase = true)
|
||||
}
|
||||
|
||||
fun hasJdkLikeUpdatedRuntime(version: String): Boolean =
|
||||
VersionComparatorUtil.compare(version, "1.2.0-rc-39") >= 0 ||
|
||||
isSnapshot(version) ||
|
||||
version == "default_version" /* for tests */
|
||||
|
||||
fun hasJreSpecificRuntime(version: String): Boolean =
|
||||
VersionComparatorUtil.compare(version, "1.1.0") >= 0 ||
|
||||
isSnapshot(version) ||
|
||||
version == "default_version" /* for tests */
|
||||
|
||||
const val MAVEN_STDLIB_ID = PathUtil.KOTLIN_JAVA_STDLIB_NAME
|
||||
|
||||
const val MAVEN_STDLIB_ID_JRE7 = PathUtil.KOTLIN_JAVA_RUNTIME_JRE7_NAME
|
||||
const val MAVEN_STDLIB_ID_JDK7 = PathUtil.KOTLIN_JAVA_RUNTIME_JDK7_NAME
|
||||
|
||||
const val MAVEN_STDLIB_ID_JRE8 = PathUtil.KOTLIN_JAVA_RUNTIME_JRE8_NAME
|
||||
const val MAVEN_STDLIB_ID_JDK8 = PathUtil.KOTLIN_JAVA_RUNTIME_JDK8_NAME
|
||||
|
||||
const val MAVEN_JS_STDLIB_ID = PathUtil.JS_LIB_NAME
|
||||
const val MAVEN_JS_TEST_ID = PathUtil.KOTLIN_TEST_JS_NAME
|
||||
|
||||
const val MAVEN_OLD_JS_STDLIB_ID = "kotlin-js-library"
|
||||
const val MAVEN_COMMON_STDLIB_ID = "kotlin-stdlib-common" // TODO: KotlinCommonMavenConfigurator
|
||||
const val MAVEN_TEST_ID = PathUtil.KOTLIN_TEST_NAME
|
||||
const val MAVEN_TEST_JUNIT_ID = "kotlin-test-junit"
|
||||
const val MAVEN_COMMON_TEST_ID = "kotlin-test-common"
|
||||
const val MAVEN_COMMON_TEST_ANNOTATIONS_ID = "kotlin-test-annotations-common"
|
||||
|
||||
val LOG = Logger.getInstance("org.jetbrains.kotlin.idea.versions.KotlinRuntimeLibraryUtilKt")
|
||||
|
||||
data class LibInfo(
|
||||
val groupId: String,
|
||||
val name: String,
|
||||
val version: String = "0.0.0"
|
||||
)
|
||||
|
||||
data class DeprecatedLibInfo(
|
||||
val old: LibInfo,
|
||||
val new: LibInfo,
|
||||
val outdatedAfterVersion: String,
|
||||
val message: String
|
||||
)
|
||||
|
||||
private fun deprecatedLib(
|
||||
oldGroupId: String,
|
||||
oldName: String,
|
||||
newGroupId: String = oldGroupId,
|
||||
newName: String = oldName,
|
||||
outdatedAfterVersion: String,
|
||||
message: String
|
||||
): DeprecatedLibInfo {
|
||||
return DeprecatedLibInfo(
|
||||
old = LibInfo(groupId = oldGroupId, name = oldName),
|
||||
new = LibInfo(groupId = newGroupId, name = newName),
|
||||
outdatedAfterVersion = outdatedAfterVersion,
|
||||
message = message
|
||||
)
|
||||
}
|
||||
|
||||
val DEPRECATED_LIBRARIES_INFORMATION = listOf(
|
||||
deprecatedLib(
|
||||
oldGroupId = "org.jetbrains.kotlin",
|
||||
oldName = PathUtil.KOTLIN_JAVA_RUNTIME_JRE7_NAME, newName = PathUtil.KOTLIN_JAVA_RUNTIME_JDK7_NAME,
|
||||
outdatedAfterVersion = "1.2.0-rc-39",
|
||||
message = "${PathUtil.KOTLIN_JAVA_RUNTIME_JRE7_NAME} is deprecated since 1.2.0 and should be replaced with ${PathUtil.KOTLIN_JAVA_RUNTIME_JDK7_NAME}"
|
||||
),
|
||||
|
||||
deprecatedLib(
|
||||
oldGroupId = "org.jetbrains.kotlin",
|
||||
oldName = PathUtil.KOTLIN_JAVA_RUNTIME_JRE8_NAME, newName = PathUtil.KOTLIN_JAVA_RUNTIME_JDK8_NAME,
|
||||
outdatedAfterVersion = "1.2.0-rc-39",
|
||||
message = "${PathUtil.KOTLIN_JAVA_RUNTIME_JRE8_NAME} is deprecated since 1.2.0 and should be replaced with ${PathUtil.KOTLIN_JAVA_RUNTIME_JDK8_NAME}"
|
||||
)
|
||||
)
|
||||
@@ -1,52 +0,0 @@
|
||||
operator fun Any.get(a: Int) {
|
||||
if (a > 0) {
|
||||
<lineMarker descr="Recursive call">this[a - 1]</lineMarker>
|
||||
}
|
||||
}
|
||||
|
||||
class A {
|
||||
override fun <lineMarker descr="Overrides function in 'Any'">equals</lineMarker>(other: Any?): Boolean {
|
||||
this <lineMarker descr="Recursive call">==</lineMarker> other
|
||||
return true
|
||||
}
|
||||
|
||||
operator fun inc(): A {
|
||||
this<lineMarker descr="Recursive call">++</lineMarker>
|
||||
<lineMarker descr="Recursive call">++</lineMarker>this
|
||||
return this
|
||||
}
|
||||
|
||||
operator fun component1(): Int {
|
||||
// TODO: should be recursion marker too
|
||||
val (a) = this
|
||||
return 1
|
||||
}
|
||||
|
||||
operator fun unaryPlus() {
|
||||
<lineMarker descr="Recursive call">+</lineMarker>this
|
||||
}
|
||||
|
||||
operator fun unaryMinus() {
|
||||
<lineMarker descr="Recursive call">-</lineMarker>this
|
||||
}
|
||||
|
||||
operator fun plus(a: Int) {
|
||||
this <lineMarker descr="Recursive call">+</lineMarker> 1
|
||||
this += 1
|
||||
}
|
||||
|
||||
operator fun invoke() {
|
||||
val a = A()
|
||||
a()
|
||||
a.invoke()
|
||||
|
||||
this.<lineMarker descr="Recursive call">invoke</lineMarker>()
|
||||
<lineMarker descr="Recursive call">this</lineMarker>()
|
||||
}
|
||||
}
|
||||
|
||||
class B
|
||||
operator fun B.invoke() {
|
||||
<lineMarker descr="Recursive call">this</lineMarker>()
|
||||
<lineMarker descr="Recursive call">invoke</lineMarker>()
|
||||
}
|
||||
Vendored
Vendored
Vendored
Vendored
Vendored
Vendored
Vendored
Vendored
-12
@@ -1,12 +0,0 @@
|
||||
// "Create type parameter 'Test' in class 'C'" "false"
|
||||
// ACTION: Add 'testng' to classpath
|
||||
// ACTION: Create annotation 'Test'
|
||||
// ACTION: Make internal
|
||||
// ACTION: Make private
|
||||
// ACTION: Make protected
|
||||
// ERROR: Unresolved reference: Test
|
||||
class C {
|
||||
@<caret>Test fun foo() {
|
||||
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user