Remove 172 bunchset

This commit is contained in:
Vyacheslav Gerasimov
2018-08-02 19:08:04 +03:00
parent 0c867b4804
commit a2bf417d75
338 changed files with 0 additions and 26220 deletions
-1
View File
@@ -1,6 +1,5 @@
182
181
173_181
172_173_181
as31_173_181
as32_181
@@ -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) {
}
}
-221
View File
@@ -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 { *; }
@@ -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
}
}
}
@@ -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));
}
}
@@ -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);
}
};
}
@@ -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
@@ -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>
}
@@ -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
@@ -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"
@@ -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 &amp;Frameworks:"/>
</properties>
</component>
</children>
</grid>
</children>
</grid>
</children>
</grid>
</children>
</grid>
</form>
@@ -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;
}
}
@@ -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>
@@ -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);
}
}
@@ -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 : "";}
}
@@ -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;
}
}
@@ -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\")");
}
}
@@ -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\")");
}
}
@@ -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-"))
}
@@ -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) }
}
@@ -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) {}
@@ -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"
}
@@ -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\")")
}
}
@@ -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)
}
}
}
}
@@ -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()
}
}
@@ -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)
}
}
@@ -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;
}
}
@@ -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
)
}
}
@@ -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>
-98
View File
@@ -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
@@ -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
}
@@ -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")
}
}
}
}
@@ -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)
}
}
@@ -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
}
@@ -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
)
}
@@ -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)
}
@@ -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)
}
}
@@ -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)
}
}
@@ -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))
}
}
}
}
@@ -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
}
}
@@ -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)
}
}
@@ -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
)
}
}
@@ -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)
}
}
@@ -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)
}
}
@@ -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)
}
}
@@ -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)
}
}
@@ -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
}
}
@@ -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
)
}
}
@@ -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)
}
}
@@ -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"
@@ -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) }
}
}
}
@@ -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>()
}
@@ -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