Switch to 181 platform
This commit is contained in:
@@ -95,6 +95,7 @@ dependencies {
|
||||
testRuntime(intellijPluginDep("coverage"))
|
||||
testRuntime(intellijPluginDep("maven"))
|
||||
testRuntime(intellijPluginDep("android"))
|
||||
testRuntime(intellijPluginDep("smali"))
|
||||
testRuntime(intellijPluginDep("testng"))
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,126 @@
|
||||
import org.gradle.jvm.tasks.Jar
|
||||
|
||||
plugins {
|
||||
kotlin("jvm")
|
||||
id("jps-compatible")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
testRuntime(intellijDep())
|
||||
|
||||
compile(projectDist(":kotlin-stdlib"))
|
||||
compileOnly(project(":kotlin-reflect-api"))
|
||||
compile(project(":core:descriptors"))
|
||||
compile(project(":core:descriptors.jvm"))
|
||||
compile(project(":compiler:backend"))
|
||||
compile(project(":compiler:cli-common"))
|
||||
compile(project(":compiler:frontend"))
|
||||
compile(project(":compiler:frontend.java"))
|
||||
compile(project(":compiler:frontend.script"))
|
||||
compile(project(":js:js.frontend"))
|
||||
compile(project(":js:js.serializer"))
|
||||
compile(project(":compiler:light-classes"))
|
||||
compile(project(":compiler:util"))
|
||||
compile(project(":kotlin-build-common"))
|
||||
compile(project(":compiler:daemon-common"))
|
||||
compile(projectRuntimeJar(":kotlin-daemon-client"))
|
||||
compile(project(":kotlin-compiler-runner")) { isTransitive = false }
|
||||
compile(project(":compiler:plugin-api"))
|
||||
compile(project(":eval4j"))
|
||||
compile(project(":j2k"))
|
||||
compile(project(":idea:formatter"))
|
||||
compile(project(":idea:idea-core"))
|
||||
compile(project(":idea:ide-common"))
|
||||
compile(project(":idea:idea-jps-common"))
|
||||
compile(project(":idea:kotlin-gradle-tooling"))
|
||||
compile(project(":plugins:uast-kotlin"))
|
||||
compile(project(":plugins:uast-kotlin-idea"))
|
||||
compile(project(":kotlin-script-util")) { isTransitive = false }
|
||||
|
||||
compile(commonDep("org.jetbrains.kotlinx", "kotlinx-coroutines-core")) { isTransitive = false }
|
||||
compile(commonDep("org.jetbrains", "markdown"))
|
||||
|
||||
compileOnly(project(":kotlin-daemon-client"))
|
||||
|
||||
compileOnly(intellijDep())
|
||||
compileOnly(commonDep("com.google.code.findbugs", "jsr305"))
|
||||
compileOnly(intellijPluginDep("IntelliLang"))
|
||||
compileOnly(intellijPluginDep("copyright"))
|
||||
compileOnly(intellijPluginDep("properties"))
|
||||
compileOnly(intellijPluginDep("java-i18n"))
|
||||
|
||||
testCompile(project(":kotlin-test:kotlin-test-junit"))
|
||||
testCompile(projectTests(":compiler:tests-common"))
|
||||
testCompile(projectTests(":idea:idea-test-framework")) { isTransitive = false }
|
||||
testCompile(project(":idea:idea-jvm")) { isTransitive = false }
|
||||
testCompile(project(":idea:idea-gradle")) { isTransitive = false }
|
||||
testCompile(project(":idea:idea-maven")) { isTransitive = false }
|
||||
testCompile(commonDep("junit:junit"))
|
||||
|
||||
testRuntime(project(":plugins:kapt3-idea")) { isTransitive = false }
|
||||
testRuntime(projectDist(":kotlin-reflect"))
|
||||
testRuntime(projectDist(":kotlin-preloader"))
|
||||
|
||||
testCompile(project(":kotlin-sam-with-receiver-compiler-plugin")) { isTransitive = false }
|
||||
|
||||
testRuntime(project(":plugins:android-extensions-compiler"))
|
||||
testRuntime(project(":plugins:android-extensions-ide")) { isTransitive = false }
|
||||
testRuntime(project(":allopen-ide-plugin")) { isTransitive = false }
|
||||
testRuntime(project(":kotlin-allopen-compiler-plugin"))
|
||||
testRuntime(project(":noarg-ide-plugin")) { isTransitive = false }
|
||||
testRuntime(project(":kotlin-noarg-compiler-plugin"))
|
||||
testRuntime(project(":plugins:annotation-based-compiler-plugins-ide-support")) { isTransitive = false }
|
||||
testRuntime(project(":sam-with-receiver-ide-plugin")) { isTransitive = false }
|
||||
testRuntime(project(":idea:idea-android")) { isTransitive = false }
|
||||
testRuntime(project(":plugins:lint")) { isTransitive = false }
|
||||
testRuntime(project(":plugins:uast-kotlin"))
|
||||
|
||||
(rootProject.extra["compilerModules"] as Array<String>).forEach {
|
||||
testRuntime(project(it))
|
||||
}
|
||||
|
||||
testCompile(intellijPluginDep("IntelliLang"))
|
||||
testCompile(intellijPluginDep("copyright"))
|
||||
testCompile(intellijPluginDep("properties"))
|
||||
testCompile(intellijPluginDep("java-i18n"))
|
||||
testCompileOnly(intellijDep())
|
||||
testCompileOnly(commonDep("com.google.code.findbugs", "jsr305"))
|
||||
testCompileOnly(intellijPluginDep("gradle"))
|
||||
testCompileOnly(intellijPluginDep("Groovy"))
|
||||
testCompileOnly(intellijPluginDep("maven"))
|
||||
|
||||
testRuntime(intellijPluginDep("junit"))
|
||||
testRuntime(intellijPluginDep("gradle"))
|
||||
testRuntime(intellijPluginDep("Groovy"))
|
||||
testRuntime(intellijPluginDep("coverage"))
|
||||
testRuntime(intellijPluginDep("maven"))
|
||||
testRuntime(intellijPluginDep("android"))
|
||||
testRuntime(intellijPluginDep("testng"))
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
"main" {
|
||||
projectDefault()
|
||||
java.srcDirs("idea-completion/src",
|
||||
"idea-live-templates/src",
|
||||
"idea-repl/src")
|
||||
resources.srcDirs("idea-repl/src").apply { include("META-INF/**") }
|
||||
}
|
||||
"test" {
|
||||
projectDefault()
|
||||
java.srcDirs(
|
||||
"idea-completion/tests",
|
||||
"idea-live-templates/tests")
|
||||
}
|
||||
}
|
||||
|
||||
projectTest {
|
||||
dependsOn(":dist")
|
||||
workingDir = rootDir
|
||||
}
|
||||
|
||||
testsJar {}
|
||||
|
||||
classesDirsArtifact()
|
||||
configureInstrumentation()
|
||||
|
||||
+2
-2
@@ -35,7 +35,7 @@ import com.intellij.util.xmlb.XmlSerializerUtil
|
||||
return this
|
||||
}
|
||||
|
||||
override fun loadState(state: KotlinCompilerWorkspaceSettings?) {
|
||||
XmlSerializerUtil.copyBean(state!!, this)
|
||||
override fun loadState(state: KotlinCompilerWorkspaceSettings) {
|
||||
XmlSerializerUtil.copyBean(state, this)
|
||||
}
|
||||
}
|
||||
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* 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.compiler.configuration
|
||||
|
||||
import com.intellij.openapi.components.PersistentStateComponent
|
||||
import com.intellij.openapi.components.State
|
||||
import com.intellij.openapi.components.Storage
|
||||
import com.intellij.openapi.components.StoragePathMacros
|
||||
import com.intellij.util.xmlb.XmlSerializerUtil
|
||||
|
||||
@State(
|
||||
name = "KotlinCompilerWorkspaceSettings",
|
||||
storages = arrayOf(
|
||||
Storage(file = StoragePathMacros.WORKSPACE_FILE)
|
||||
)
|
||||
) class KotlinCompilerWorkspaceSettings : PersistentStateComponent<KotlinCompilerWorkspaceSettings> {
|
||||
var preciseIncrementalEnabled: Boolean = true
|
||||
var enableDaemon: Boolean = true
|
||||
|
||||
override fun getState(): KotlinCompilerWorkspaceSettings {
|
||||
return this
|
||||
}
|
||||
|
||||
override fun loadState(state: KotlinCompilerWorkspaceSettings?) {
|
||||
XmlSerializerUtil.copyBean(state!!, this)
|
||||
}
|
||||
}
|
||||
@@ -47,6 +47,7 @@ dependencies {
|
||||
testRuntime(project(":allopen-ide-plugin"))
|
||||
|
||||
testRuntime(intellijPluginDep("android"))
|
||||
testRuntime(intellijPluginDep("smali"))
|
||||
testRuntime(intellijPluginDep("copyright"))
|
||||
testRuntime(intellijPluginDep("coverage"))
|
||||
testRuntime(intellijPluginDep("gradle"))
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
|
||||
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
||||
|
||||
plugins {
|
||||
kotlin("jvm")
|
||||
id("jps-compatible")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
testRuntime(intellijDep())
|
||||
|
||||
compileOnly(project(":kotlin-reflect-api"))
|
||||
compile(project(":compiler:util"))
|
||||
compile(project(":compiler:light-classes"))
|
||||
compile(project(":compiler:frontend"))
|
||||
compile(project(":compiler:frontend.java"))
|
||||
compile(project(":idea"))
|
||||
compile(project(":idea:idea-jvm"))
|
||||
compile(project(":idea:idea-core"))
|
||||
compile(project(":idea:ide-common"))
|
||||
compile(project(":idea:idea-gradle"))
|
||||
|
||||
compile(androidDxJar())
|
||||
|
||||
compileOnly(project(":kotlin-android-extensions-runtime"))
|
||||
compileOnly(intellijDep())
|
||||
compileOnly(intellijPluginDep("android"))
|
||||
|
||||
testCompile(projectDist(":kotlin-test:kotlin-test-jvm"))
|
||||
testCompile(projectTests(":idea:idea-test-framework")) { isTransitive = false }
|
||||
testCompile(project(":plugins:lint")) { isTransitive = false }
|
||||
testCompile(project(":idea:idea-jvm"))
|
||||
testCompile(projectTests(":compiler:tests-common"))
|
||||
testCompile(projectTests(":idea"))
|
||||
testCompile(projectTests(":idea:idea-gradle"))
|
||||
testCompile(commonDep("junit:junit"))
|
||||
|
||||
testCompile(intellijDep())
|
||||
testCompile(intellijPluginDep("properties"))
|
||||
testCompileOnly(intellijPluginDep("android"))
|
||||
|
||||
testRuntime(projectDist(":kotlin-reflect"))
|
||||
testRuntime(project(":plugins:android-extensions-ide"))
|
||||
testRuntime(project(":plugins:kapt3-idea"))
|
||||
testRuntime(project(":sam-with-receiver-ide-plugin"))
|
||||
testRuntime(project(":noarg-ide-plugin"))
|
||||
testRuntime(project(":allopen-ide-plugin"))
|
||||
|
||||
testRuntime(intellijPluginDep("android"))
|
||||
testRuntime(intellijPluginDep("copyright"))
|
||||
testRuntime(intellijPluginDep("coverage"))
|
||||
testRuntime(intellijPluginDep("gradle"))
|
||||
testRuntime(intellijPluginDep("Groovy"))
|
||||
testRuntime(intellijPluginDep("IntelliLang"))
|
||||
testRuntime(intellijPluginDep("java-decompiler"))
|
||||
testRuntime(intellijPluginDep("java-i18n"))
|
||||
testRuntime(intellijPluginDep("junit"))
|
||||
testRuntime(intellijPluginDep("maven"))
|
||||
testRuntime(intellijPluginDep("testng"))
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
"main" { projectDefault() }
|
||||
"test" { projectDefault() }
|
||||
}
|
||||
|
||||
projectTest {
|
||||
workingDir = rootDir
|
||||
useAndroidSdk()
|
||||
}
|
||||
|
||||
testsJar {}
|
||||
|
||||
+3
-1
@@ -34,6 +34,7 @@ import com.intellij.ui.awt.RelativePoint
|
||||
import com.intellij.util.Function
|
||||
import org.jetbrains.android.dom.manifest.Manifest
|
||||
import org.jetbrains.android.facet.AndroidFacet
|
||||
import org.jetbrains.android.resourceManagers.ModuleResourceManagers
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.unsafeResolveToDescriptor
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
@@ -95,7 +96,8 @@ class KotlinAndroidLineMarkerProvider : LineMarkerProvider {
|
||||
return
|
||||
}
|
||||
|
||||
val files = androidFacet
|
||||
val files = ModuleResourceManagers
|
||||
.getInstance(androidFacet)
|
||||
.localResourceManager
|
||||
.findResourcesByFieldName(resClassName, info.fieldName)
|
||||
.filterIsInstance<PsiFile>()
|
||||
|
||||
+135
@@ -0,0 +1,135 @@
|
||||
/*
|
||||
* 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.android
|
||||
|
||||
import com.android.SdkConstants
|
||||
import com.android.resources.ResourceType
|
||||
import com.intellij.codeHighlighting.Pass
|
||||
import com.intellij.codeInsight.daemon.GutterIconNavigationHandler
|
||||
import com.intellij.codeInsight.daemon.LineMarkerInfo
|
||||
import com.intellij.codeInsight.daemon.LineMarkerProvider
|
||||
import com.intellij.codeInsight.navigation.NavigationUtil
|
||||
import com.intellij.icons.AllIcons
|
||||
import com.intellij.ide.highlighter.XmlFileType
|
||||
import com.intellij.navigation.GotoRelatedItem
|
||||
import com.intellij.openapi.editor.markup.GutterIconRenderer
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.PsiFile
|
||||
import com.intellij.psi.xml.XmlAttributeValue
|
||||
import com.intellij.ui.awt.RelativePoint
|
||||
import com.intellij.util.Function
|
||||
import org.jetbrains.android.dom.manifest.Manifest
|
||||
import org.jetbrains.android.facet.AndroidFacet
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.unsafeResolveToDescriptor
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
|
||||
import java.awt.event.MouseEvent
|
||||
import javax.swing.Icon
|
||||
|
||||
|
||||
class KotlinAndroidLineMarkerProvider : LineMarkerProvider {
|
||||
override fun getLineMarkerInfo(element: PsiElement): LineMarkerInfo<*>? = null
|
||||
|
||||
override fun collectSlowLineMarkers(elements: List<PsiElement>, result: MutableCollection<LineMarkerInfo<PsiElement>>) {
|
||||
elements.forEach {
|
||||
(it as? KtClass)?.getLineMarkerInfo()?.let { marker -> result.add(marker) }
|
||||
}
|
||||
}
|
||||
|
||||
private fun KtClass.getLineMarkerInfo(): LineMarkerInfo<PsiElement>? {
|
||||
val nameIdentifier = nameIdentifier ?: return null
|
||||
val androidFacet = AndroidFacet.getInstance(this) ?: return null
|
||||
val manifest = androidFacet.manifest ?: return null
|
||||
val manifestItems = collectGoToRelatedManifestItems(manifest)
|
||||
if (manifestItems.isEmpty() && !isClassWithLayoutXml()) {
|
||||
return null
|
||||
}
|
||||
|
||||
return LineMarkerInfo(
|
||||
nameIdentifier,
|
||||
nameIdentifier.textRange,
|
||||
AllIcons.FileTypes.Xml,
|
||||
Pass.LINE_MARKERS,
|
||||
Function { "Related XML file" },
|
||||
GutterIconNavigationHandler { e: MouseEvent, _: PsiElement ->
|
||||
NavigationUtil
|
||||
.getRelatedItemsPopup(
|
||||
manifestItems + collectGoToRelatedLayoutItems(androidFacet),
|
||||
"Go to Related Files")
|
||||
.show(RelativePoint(e))
|
||||
},
|
||||
GutterIconRenderer.Alignment.RIGHT)
|
||||
}
|
||||
|
||||
private fun KtClass.collectGoToRelatedLayoutItems(androidFacet: AndroidFacet): List<GotoRelatedItem> {
|
||||
val resources = mutableSetOf<PsiFile>()
|
||||
accept(object: KtVisitorVoid() {
|
||||
override fun visitKtElement(element: KtElement) {
|
||||
element.acceptChildren(this)
|
||||
}
|
||||
|
||||
override fun visitReferenceExpression(expression: KtReferenceExpression) {
|
||||
super.visitReferenceExpression(expression)
|
||||
|
||||
val resClassName = ResourceType.LAYOUT.getName()
|
||||
val info = (expression as? KtSimpleNameExpression)?.let {
|
||||
getReferredResourceOrManifestField(androidFacet, it, resClassName, true)
|
||||
}
|
||||
|
||||
if (info == null || info.isFromManifest) {
|
||||
return
|
||||
}
|
||||
|
||||
val files = androidFacet
|
||||
.localResourceManager
|
||||
.findResourcesByFieldName(resClassName, info.fieldName)
|
||||
.filterIsInstance<PsiFile>()
|
||||
|
||||
resources.addAll(files)
|
||||
}
|
||||
})
|
||||
|
||||
return resources.map { GotoRelatedLayoutItem(it) }
|
||||
}
|
||||
|
||||
private fun KtClass.collectGoToRelatedManifestItems(manifest: Manifest): List<GotoRelatedItem> =
|
||||
findComponentDeclarationInManifest(manifest)?.xmlAttributeValue?.let { listOf(GotoManifestItem(it)) } ?: emptyList()
|
||||
|
||||
private class GotoManifestItem(attributeValue: XmlAttributeValue) : GotoRelatedItem(attributeValue) {
|
||||
override fun getCustomName(): String? = "AndroidManifest.xml"
|
||||
override fun getCustomContainerName(): String? = ""
|
||||
override fun getCustomIcon(): Icon? = XmlFileType.INSTANCE.icon
|
||||
}
|
||||
|
||||
private class GotoRelatedLayoutItem(private val file: PsiFile) : GotoRelatedItem(file, "Layout Files") {
|
||||
override fun getCustomContainerName(): String? = "(${file.containingDirectory.name})"
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val CLASSES_WITH_LAYOUT_XML = arrayOf(
|
||||
SdkConstants.CLASS_ACTIVITY,
|
||||
SdkConstants.CLASS_FRAGMENT,
|
||||
SdkConstants.CLASS_V4_FRAGMENT,
|
||||
"android.widget.Adapter")
|
||||
|
||||
private fun KtClass.isClassWithLayoutXml(): Boolean {
|
||||
val type = (unsafeResolveToDescriptor(BodyResolveMode.PARTIAL) as? ClassDescriptor)?.defaultType ?: return false
|
||||
return CLASSES_WITH_LAYOUT_XML.any { type.isSubclassOf(it, true) }
|
||||
}
|
||||
}
|
||||
}
|
||||
+23
-23
@@ -23,8 +23,8 @@ import com.android.ide.common.resources.ResourceRepository;
|
||||
import com.android.ide.common.resources.ResourceResolver;
|
||||
import com.android.resources.ResourceType;
|
||||
import com.android.tools.idea.configurations.Configuration;
|
||||
import com.android.tools.idea.configurations.ConfigurationManager;
|
||||
import com.android.tools.idea.res.AppResourceRepository;
|
||||
import com.android.tools.idea.res.LocalResourceRepository;
|
||||
import com.android.tools.idea.res.ResourceHelper;
|
||||
import com.android.tools.idea.ui.resourcechooser.ColorPicker;
|
||||
import com.android.utils.XmlUtils;
|
||||
@@ -61,15 +61,14 @@ import java.awt.*;
|
||||
import java.io.File;
|
||||
|
||||
import static com.android.SdkConstants.*;
|
||||
import static com.android.SdkConstants.ANDROID_URI;
|
||||
import static com.android.SdkConstants.ATTR_DRAWABLE;
|
||||
import static com.android.tools.idea.uibuilder.property.renderer.NlDefaultRenderer.ICON_SIZE;
|
||||
import static org.jetbrains.android.AndroidColorAnnotator.pickLayoutFile;
|
||||
|
||||
/**
|
||||
* Contains copied privates from AndroidColorAnnotator, so we could use them for Kotlin AndroidResourceReferenceAnnotator
|
||||
*/
|
||||
public class ResourceReferenceAnnotatorUtil {
|
||||
|
||||
public static final int ICON_SIZE = 8;
|
||||
|
||||
@Nullable
|
||||
public static File pickBitmapFromXml(@NotNull File file, @NotNull ResourceResolver resourceResolver, @NotNull Project project) {
|
||||
try {
|
||||
@@ -146,7 +145,7 @@ public class ResourceReferenceAnnotatorUtil {
|
||||
ResourceItem item = frameworkResources.getResourceItem(type, name);
|
||||
return item.getResourceValue(type, configuration.getFullConfig(), false);
|
||||
} else {
|
||||
LocalResourceRepository appResources = AppResourceRepository.getAppResources(module, true);
|
||||
AppResourceRepository appResources = AppResourceRepository.getOrCreateInstance(module);
|
||||
if (appResources == null) {
|
||||
return null;
|
||||
}
|
||||
@@ -161,26 +160,27 @@ public class ResourceReferenceAnnotatorUtil {
|
||||
@Nullable
|
||||
public static Configuration pickConfiguration(AndroidFacet facet, Module module, PsiFile file) {
|
||||
VirtualFile virtualFile = file.getVirtualFile();
|
||||
if (virtualFile == null) {
|
||||
if(virtualFile == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
VirtualFile parent = virtualFile.getParent();
|
||||
if (parent == null) {
|
||||
return null;
|
||||
}
|
||||
VirtualFile layout;
|
||||
String parentName = parent.getName();
|
||||
if (!parentName.startsWith(FD_RES_LAYOUT)) {
|
||||
layout = pickLayoutFile(module, facet);
|
||||
if (layout == null) {
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
layout = virtualFile;
|
||||
}
|
||||
VirtualFile parent = virtualFile.getParent();
|
||||
if(parent == null) {
|
||||
return null;
|
||||
} else {
|
||||
String parentName = parent.getName();
|
||||
VirtualFile layout;
|
||||
if(!parentName.startsWith("layout")) {
|
||||
layout = ResourceHelper.pickAnyLayoutFile(module, facet);
|
||||
if(layout == null) {
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
layout = virtualFile;
|
||||
}
|
||||
|
||||
return facet.getConfigurationManager().getConfiguration(layout);
|
||||
return ConfigurationManager.getOrCreateInstance(module).getConfiguration(layout);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class ColorRenderer extends GutterIconRenderer {
|
||||
|
||||
+270
@@ -0,0 +1,270 @@
|
||||
/*
|
||||
* 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.android;
|
||||
|
||||
|
||||
import com.android.ide.common.rendering.api.ResourceValue;
|
||||
import com.android.ide.common.resources.ResourceItem;
|
||||
import com.android.ide.common.resources.ResourceRepository;
|
||||
import com.android.ide.common.resources.ResourceResolver;
|
||||
import com.android.resources.ResourceType;
|
||||
import com.android.tools.idea.configurations.Configuration;
|
||||
import com.android.tools.idea.res.AppResourceRepository;
|
||||
import com.android.tools.idea.res.LocalResourceRepository;
|
||||
import com.android.tools.idea.res.ResourceHelper;
|
||||
import com.android.tools.idea.ui.resourcechooser.ColorPicker;
|
||||
import com.android.utils.XmlUtils;
|
||||
import com.google.common.base.Charsets;
|
||||
import com.google.common.io.Files;
|
||||
import com.intellij.openapi.actionSystem.AnAction;
|
||||
import com.intellij.openapi.actionSystem.AnActionEvent;
|
||||
import com.intellij.openapi.actionSystem.CommonDataKeys;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.editor.markup.GutterIconRenderer;
|
||||
import com.intellij.openapi.module.Module;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiFile;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.psi.xml.XmlAttribute;
|
||||
import com.intellij.psi.xml.XmlAttributeValue;
|
||||
import com.intellij.psi.xml.XmlTag;
|
||||
import com.intellij.util.ui.ColorIcon;
|
||||
import com.intellij.util.ui.EmptyIcon;
|
||||
import com.intellij.util.ui.JBUI;
|
||||
import org.jetbrains.android.facet.AndroidFacet;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Element;
|
||||
import org.w3c.dom.Node;
|
||||
import org.w3c.dom.NodeList;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.io.File;
|
||||
|
||||
import static com.android.SdkConstants.*;
|
||||
import static com.android.SdkConstants.ANDROID_URI;
|
||||
import static com.android.SdkConstants.ATTR_DRAWABLE;
|
||||
import static com.android.tools.idea.uibuilder.property.renderer.NlDefaultRenderer.ICON_SIZE;
|
||||
import static org.jetbrains.android.AndroidColorAnnotator.pickLayoutFile;
|
||||
|
||||
/**
|
||||
* Contains copied privates from AndroidColorAnnotator, so we could use them for Kotlin AndroidResourceReferenceAnnotator
|
||||
*/
|
||||
public class ResourceReferenceAnnotatorUtil {
|
||||
@Nullable
|
||||
public static File pickBitmapFromXml(@NotNull File file, @NotNull ResourceResolver resourceResolver, @NotNull Project project) {
|
||||
try {
|
||||
String xml = Files.toString(file, Charsets.UTF_8);
|
||||
Document document = XmlUtils.parseDocumentSilently(xml, true);
|
||||
if (document != null && document.getDocumentElement() != null) {
|
||||
Element root = document.getDocumentElement();
|
||||
String tag = root.getTagName();
|
||||
Element target = null;
|
||||
String attribute = null;
|
||||
if ("vector".equals(tag)) {
|
||||
// Vectors are handled in the icon cache
|
||||
return file;
|
||||
}
|
||||
else if ("bitmap".equals(tag) || "nine-patch".equals(tag)) {
|
||||
target = root;
|
||||
attribute = ATTR_SRC;
|
||||
}
|
||||
else if ("selector".equals(tag) ||
|
||||
"level-list".equals(tag) ||
|
||||
"layer-list".equals(tag) ||
|
||||
"transition".equals(tag)) {
|
||||
NodeList children = root.getChildNodes();
|
||||
for (int i = children.getLength() - 1; i >= 0; i--) {
|
||||
Node item = children.item(i);
|
||||
if (item.getNodeType() == Node.ELEMENT_NODE && TAG_ITEM.equals(item.getNodeName())) {
|
||||
target = (Element)item;
|
||||
if (target.hasAttributeNS(ANDROID_URI, ATTR_DRAWABLE)) {
|
||||
attribute = ATTR_DRAWABLE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if ("clip".equals(tag) || "inset".equals(tag) || "scale".equals(tag)) {
|
||||
target = root;
|
||||
attribute = ATTR_DRAWABLE;
|
||||
} else {
|
||||
// <shape> etc - no bitmap to be found
|
||||
return null;
|
||||
}
|
||||
if (attribute != null && target.hasAttributeNS(ANDROID_URI, attribute)) {
|
||||
String src = target.getAttributeNS(ANDROID_URI, attribute);
|
||||
ResourceValue value = resourceResolver.findResValue(src, false);
|
||||
if (value != null) {
|
||||
return ResourceHelper.resolveDrawable(resourceResolver, value, project);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Throwable ignore) {
|
||||
// Not logging for now; afraid to risk unexpected crashes in upcoming preview. TODO: Re-enable.
|
||||
//Logger.getInstance(AndroidColorAnnotator.class).warn(String.format("Could not read/render icon image %1$s", file), e);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/** Looks up the resource item of the given type and name for the given configuration, if any */
|
||||
@Nullable
|
||||
public static ResourceValue findResourceValue(ResourceType type,
|
||||
String name,
|
||||
boolean isFramework,
|
||||
Module module,
|
||||
Configuration configuration) {
|
||||
if (isFramework) {
|
||||
ResourceRepository frameworkResources = configuration.getFrameworkResources();
|
||||
if (frameworkResources == null) {
|
||||
return null;
|
||||
}
|
||||
if (!frameworkResources.hasResourceItem(type, name)) {
|
||||
return null;
|
||||
}
|
||||
ResourceItem item = frameworkResources.getResourceItem(type, name);
|
||||
return item.getResourceValue(type, configuration.getFullConfig(), false);
|
||||
} else {
|
||||
LocalResourceRepository appResources = AppResourceRepository.getAppResources(module, true);
|
||||
if (appResources == null) {
|
||||
return null;
|
||||
}
|
||||
if (!appResources.hasResourceItem(type, name)) {
|
||||
return null;
|
||||
}
|
||||
return appResources.getConfiguredValue(type, name, configuration.getFullConfig());
|
||||
}
|
||||
}
|
||||
|
||||
/** Picks a suitable configuration to use for resource resolution */
|
||||
@Nullable
|
||||
public static Configuration pickConfiguration(AndroidFacet facet, Module module, PsiFile file) {
|
||||
VirtualFile virtualFile = file.getVirtualFile();
|
||||
if (virtualFile == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
VirtualFile parent = virtualFile.getParent();
|
||||
if (parent == null) {
|
||||
return null;
|
||||
}
|
||||
VirtualFile layout;
|
||||
String parentName = parent.getName();
|
||||
if (!parentName.startsWith(FD_RES_LAYOUT)) {
|
||||
layout = pickLayoutFile(module, facet);
|
||||
if (layout == null) {
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
layout = virtualFile;
|
||||
}
|
||||
|
||||
return facet.getConfigurationManager().getConfiguration(layout);
|
||||
}
|
||||
|
||||
public static class ColorRenderer extends GutterIconRenderer {
|
||||
private final PsiElement myElement;
|
||||
private final Color myColor;
|
||||
|
||||
ColorRenderer(@NotNull PsiElement element, @Nullable Color color) {
|
||||
myElement = element;
|
||||
myColor = color;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Icon getIcon() {
|
||||
Color color = getCurrentColor();
|
||||
return JBUI.scale(color == null ? EmptyIcon.create(ICON_SIZE) : new ColorIcon(ICON_SIZE, color));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private Color getCurrentColor() {
|
||||
if (myColor != null) {
|
||||
return myColor;
|
||||
} else if (myElement instanceof XmlTag) {
|
||||
return ResourceHelper.parseColor(((XmlTag)myElement).getValue().getText());
|
||||
} else if (myElement instanceof XmlAttributeValue) {
|
||||
return ResourceHelper.parseColor(((XmlAttributeValue)myElement).getValue());
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public AnAction getClickAction() {
|
||||
if (myColor != null) { // Cannot set colors that were derived
|
||||
return null;
|
||||
}
|
||||
return new AnAction() {
|
||||
@Override
|
||||
public void actionPerformed(AnActionEvent e) {
|
||||
final Editor editor = CommonDataKeys.EDITOR.getData(e.getDataContext());
|
||||
if (editor != null) {
|
||||
// Need ARGB support in platform color chooser; see
|
||||
// https://youtrack.jetbrains.com/issue/IDEA-123498
|
||||
//final Color color =
|
||||
// ColorChooser.chooseColor(editor.getComponent(), AndroidBundle.message("android.choose.color"), getCurrentColor());
|
||||
final Color color = ColorPicker.showDialog(editor.getComponent(), "Choose Color", getCurrentColor(), true, null, false);
|
||||
if (color != null) {
|
||||
ApplicationManager.getApplication().runWriteAction(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (myElement instanceof XmlTag) {
|
||||
((XmlTag)myElement).getValue().setText(ResourceHelper.colorToString(color));
|
||||
} else if (myElement instanceof XmlAttributeValue) {
|
||||
XmlAttribute attribute = PsiTreeUtil.getParentOfType(myElement, XmlAttribute.class);
|
||||
if (attribute != null) {
|
||||
attribute.setValue(ResourceHelper.colorToString(color));
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
ColorRenderer that = (ColorRenderer)o;
|
||||
// TODO: Compare with modification count in app resources (if not framework)
|
||||
if (myColor != null ? !myColor.equals(that.myColor) : that.myColor != null) return false;
|
||||
if (!myElement.equals(that.myElement)) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = myElement.hashCode();
|
||||
result = 31 * result + (myColor != null ? myColor.hashCode() : 0);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -22,6 +22,7 @@ import com.intellij.openapi.roots.ProjectRootModificationTracker
|
||||
import com.intellij.psi.util.CachedValueProvider
|
||||
import com.intellij.psi.util.CachedValuesManager
|
||||
import org.jetbrains.android.facet.AndroidFacet
|
||||
import org.jetbrains.android.sdk.AndroidSdkData
|
||||
import org.jetbrains.kotlin.idea.debugger.evaluate.classLoading.AndroidDexer
|
||||
import org.jetbrains.kotlin.idea.debugger.evaluate.classLoading.ClassToLoad
|
||||
import java.io.File
|
||||
@@ -56,7 +57,7 @@ class AndroidDexerImpl(val project: Project) : AndroidDexer {
|
||||
private fun doGetAndroidDexFile(): File? {
|
||||
for (module in ModuleManager.getInstance(project).modules) {
|
||||
val androidFacet = AndroidFacet.getInstance(module) ?: continue
|
||||
val sdkData = androidFacet.sdkData ?: continue
|
||||
val sdkData = AndroidSdkData.getSdkData(androidFacet) ?: continue
|
||||
val latestBuildTool = sdkData.getLatestBuildTool(/* allowPreview = */ false)
|
||||
?: sdkData.getLatestBuildTool(/* allowPreview = */ true)
|
||||
?: continue
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* 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.android.debugger
|
||||
|
||||
import com.intellij.openapi.module.ModuleManager
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.roots.ProjectRootModificationTracker
|
||||
import com.intellij.psi.util.CachedValueProvider
|
||||
import com.intellij.psi.util.CachedValuesManager
|
||||
import org.jetbrains.android.facet.AndroidFacet
|
||||
import org.jetbrains.kotlin.idea.debugger.evaluate.classLoading.AndroidDexer
|
||||
import org.jetbrains.kotlin.idea.debugger.evaluate.classLoading.ClassToLoad
|
||||
import java.io.File
|
||||
import java.net.URLClassLoader
|
||||
import java.security.ProtectionDomain
|
||||
|
||||
class AndroidDexerImpl(val project: Project) : AndroidDexer {
|
||||
private val cachedDexWrapper = CachedValuesManager.getManager(project).createCachedValue({
|
||||
val dexWrapper = doGetAndroidDexFile()?.let { dexJarFile ->
|
||||
val androidDexWrapperName = AndroidDexWrapper::class.java.canonicalName
|
||||
val classBytes = this.javaClass.classLoader.getResource(
|
||||
androidDexWrapperName.replace('.', '/') + ".class").readBytes()
|
||||
|
||||
val dexClassLoader = object : URLClassLoader(arrayOf(dexJarFile.toURI().toURL()), this::class.java.classLoader) {
|
||||
init {
|
||||
defineClass(androidDexWrapperName, classBytes, 0, classBytes.size, null as ProtectionDomain?)
|
||||
}
|
||||
}
|
||||
|
||||
Class.forName(androidDexWrapperName, true, dexClassLoader).newInstance()
|
||||
}
|
||||
|
||||
CachedValueProvider.Result.createSingleDependency(dexWrapper, ProjectRootModificationTracker.getInstance(project))
|
||||
}, /* trackValue = */ false)
|
||||
|
||||
override fun dex(classes: Collection<ClassToLoad>): ByteArray? {
|
||||
val dexWrapper = cachedDexWrapper.value
|
||||
val dexMethod = dexWrapper::class.java.methods.firstOrNull { it.name == "dex" } ?: return null
|
||||
return dexMethod.invoke(dexWrapper, classes) as? ByteArray ?: return null
|
||||
}
|
||||
|
||||
private fun doGetAndroidDexFile(): File? {
|
||||
for (module in ModuleManager.getInstance(project).modules) {
|
||||
val androidFacet = AndroidFacet.getInstance(module) ?: continue
|
||||
val sdkData = androidFacet.sdkData ?: continue
|
||||
val latestBuildTool = sdkData.getLatestBuildTool(/* allowPreview = */ false)
|
||||
?: sdkData.getLatestBuildTool(/* allowPreview = */ true)
|
||||
?: continue
|
||||
|
||||
val dxJar = File(latestBuildTool.location, "lib/dx.jar")
|
||||
if (dxJar.exists()) {
|
||||
return dxJar
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
}
|
||||
+1
-2
@@ -47,7 +47,6 @@ class ResourceFoldingBuilder : FoldingBuilderEx() {
|
||||
// See lint's StringFormatDetector
|
||||
private val FORMAT = Pattern.compile("%(\\d+\\$)?([-+#, 0(<]*)?(\\d+)?(\\.\\d+)?([tT])?([a-zA-Z%])")
|
||||
private val FOLD_MAX_LENGTH = 60
|
||||
private val FORCE_PROJECT_RESOURCE_LOADING = true
|
||||
private val UNIT_TEST_MODE: Boolean = ApplicationManager.getApplication().isUnitTestMode
|
||||
private val RESOURCE_TYPES = listOf(ResourceType.STRING,
|
||||
ResourceType.DIMEN,
|
||||
@@ -254,6 +253,6 @@ class ResourceFoldingBuilder : FoldingBuilderEx() {
|
||||
}
|
||||
|
||||
private fun getAppResources(element: PsiElement): LocalResourceRepository? = ModuleUtilCore.findModuleForPsiElement(element)?.let {
|
||||
AppResourceRepository.getAppResources(it, FORCE_PROJECT_RESOURCE_LOADING)
|
||||
AppResourceRepository.findExistingInstance(it)
|
||||
}
|
||||
}
|
||||
|
||||
+259
@@ -0,0 +1,259 @@
|
||||
/*
|
||||
* 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.android.folding
|
||||
|
||||
import com.android.SdkConstants.*
|
||||
import com.android.ide.common.resources.configuration.FolderConfiguration
|
||||
import com.android.ide.common.resources.configuration.LocaleQualifier
|
||||
import com.android.resources.ResourceType
|
||||
import com.android.tools.idea.folding.AndroidFoldingSettings
|
||||
import com.android.tools.idea.res.AppResourceRepository
|
||||
import com.android.tools.idea.res.LocalResourceRepository
|
||||
import com.intellij.lang.ASTNode
|
||||
import com.intellij.lang.folding.FoldingBuilderEx
|
||||
import com.intellij.lang.folding.FoldingDescriptor
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.components.ServiceManager
|
||||
import com.intellij.openapi.editor.Document
|
||||
import com.intellij.openapi.module.ModuleUtilCore
|
||||
import com.intellij.openapi.util.text.StringUtil
|
||||
import com.intellij.psi.PsiClass
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.impl.source.SourceTreeToPsiMap
|
||||
import org.jetbrains.android.facet.AndroidFacet
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.uast.*
|
||||
import org.jetbrains.uast.visitor.AbstractUastVisitor
|
||||
import java.util.regex.Pattern
|
||||
|
||||
|
||||
class ResourceFoldingBuilder : FoldingBuilderEx() {
|
||||
|
||||
companion object {
|
||||
// See lint's StringFormatDetector
|
||||
private val FORMAT = Pattern.compile("%(\\d+\\$)?([-+#, 0(<]*)?(\\d+)?(\\.\\d+)?([tT])?([a-zA-Z%])")
|
||||
private val FOLD_MAX_LENGTH = 60
|
||||
private val FORCE_PROJECT_RESOURCE_LOADING = true
|
||||
private val UNIT_TEST_MODE: Boolean = ApplicationManager.getApplication().isUnitTestMode
|
||||
private val RESOURCE_TYPES = listOf(ResourceType.STRING,
|
||||
ResourceType.DIMEN,
|
||||
ResourceType.INTEGER,
|
||||
ResourceType.PLURALS)
|
||||
}
|
||||
|
||||
private val isFoldingEnabled = AndroidFoldingSettings.getInstance().isCollapseAndroidStrings
|
||||
|
||||
override fun getPlaceholderText(node: ASTNode): String? {
|
||||
|
||||
tailrec fun UElement.unwrapReferenceAndGetValue(resources: LocalResourceRepository): String? = when (this) {
|
||||
is UQualifiedReferenceExpression -> selector.unwrapReferenceAndGetValue(resources)
|
||||
is UCallExpression -> (valueArguments.firstOrNull() as? UReferenceExpression)?.getAndroidResourceValue(resources, this)
|
||||
else -> (this as? UReferenceExpression)?.getAndroidResourceValue(resources)
|
||||
}
|
||||
|
||||
val element = SourceTreeToPsiMap.treeElementToPsi(node) ?: return null
|
||||
val appResources = getAppResources(element) ?: return null
|
||||
val uastContext = ServiceManager.getService(element.project, UastContext::class.java) ?: return null
|
||||
return uastContext.convertElement(element, null, null)?.unwrapReferenceAndGetValue(appResources)
|
||||
}
|
||||
|
||||
override fun buildFoldRegions(root: PsiElement, document: Document, quick: Boolean): Array<FoldingDescriptor> {
|
||||
if (root !is KtFile || quick && !UNIT_TEST_MODE || !isFoldingEnabled || AndroidFacet.getInstance(root) == null) {
|
||||
return emptyArray()
|
||||
}
|
||||
|
||||
val file = root.toUElement()
|
||||
val result = arrayListOf<FoldingDescriptor>()
|
||||
file?.accept(object : AbstractUastVisitor() {
|
||||
override fun visitSimpleNameReferenceExpression(node: USimpleNameReferenceExpression): Boolean {
|
||||
node.getFoldingDescriptor()?.let { result.add(it) }
|
||||
return super.visitSimpleNameReferenceExpression(node)
|
||||
}
|
||||
})
|
||||
|
||||
return result.toTypedArray()
|
||||
}
|
||||
|
||||
override fun isCollapsedByDefault(node: ASTNode): Boolean = isFoldingEnabled
|
||||
|
||||
private fun UReferenceExpression.getFoldingDescriptor(): FoldingDescriptor? {
|
||||
val resolved = resolve() ?: return null
|
||||
val resourceType = resolved.getAndroidResourceType() ?: return null
|
||||
if (resourceType !in RESOURCE_TYPES) return null
|
||||
|
||||
fun UElement.createFoldingDescriptor() = psi?.let { psi ->
|
||||
val dependencies: Set<PsiElement> = setOf(psi)
|
||||
FoldingDescriptor(psi.node, psi.textRange, null, dependencies)
|
||||
}
|
||||
|
||||
val element = uastParent as? UQualifiedReferenceExpression ?: this
|
||||
val getResourceValueCall = (element.uastParent as? UCallExpression)?.takeIf { it.isFoldableGetResourceValueCall() }
|
||||
if (getResourceValueCall != null) {
|
||||
val qualifiedCall = getResourceValueCall.uastParent as? UQualifiedReferenceExpression
|
||||
if (qualifiedCall?.selector == getResourceValueCall) {
|
||||
return qualifiedCall.createFoldingDescriptor()
|
||||
}
|
||||
|
||||
return getResourceValueCall.createFoldingDescriptor()
|
||||
}
|
||||
|
||||
return element.createFoldingDescriptor()
|
||||
}
|
||||
|
||||
private fun UCallExpression.isFoldableGetResourceValueCall(): Boolean {
|
||||
return methodName == "getString" ||
|
||||
methodName == "getText" ||
|
||||
methodName == "getInteger" ||
|
||||
methodName?.startsWith("getDimension") ?: false ||
|
||||
methodName?.startsWith("getQuantityString") ?: false
|
||||
}
|
||||
|
||||
private fun PsiElement.getAndroidResourceType(): ResourceType? {
|
||||
val elementType = parent as? PsiClass ?: return null
|
||||
val elementPackage = elementType.parent as? PsiClass ?: return null
|
||||
if (R_CLASS != elementPackage.name) return null
|
||||
if (elementPackage.qualifiedName != "$ANDROID_PKG.$R_CLASS") {
|
||||
return ResourceType.getEnum(elementType.name)
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
private fun UReferenceExpression.getAndroidResourceValue(resources: LocalResourceRepository, call: UCallExpression? = null): String? {
|
||||
val resourceType = resolve()?.getAndroidResourceType() ?: return null
|
||||
val referenceConfig = FolderConfiguration().apply { localeQualifier = LocaleQualifier("xx") }
|
||||
val key = resolvedName ?: return null
|
||||
val resourceValue = resources.getResourceValue(resourceType, key, referenceConfig) ?: return null
|
||||
val text = if (call != null) formatArguments(call, resourceValue) else resourceValue
|
||||
|
||||
if (resourceType == ResourceType.STRING || resourceType == ResourceType.PLURALS) {
|
||||
return '"' + StringUtil.shortenTextWithEllipsis(text, FOLD_MAX_LENGTH - 2, 0) + '"'
|
||||
}
|
||||
else if (text.length <= 1) {
|
||||
// Don't just inline empty or one-character replacements: they can't be expanded by a mouse click
|
||||
// so are hard to use without knowing about the folding keyboard shortcut to toggle folding.
|
||||
// This is similar to how IntelliJ 14 handles call parameters
|
||||
return key + ": " + text
|
||||
}
|
||||
|
||||
return StringUtil.shortenTextWithEllipsis(text, FOLD_MAX_LENGTH, 0)
|
||||
}
|
||||
|
||||
private tailrec fun LocalResourceRepository.getResourceValue(
|
||||
type: ResourceType,
|
||||
name: String,
|
||||
referenceConfig: FolderConfiguration): String? {
|
||||
val value = getConfiguredValue(type, name, referenceConfig)?.value ?: return null
|
||||
if (!value.startsWith('@')) {
|
||||
return value
|
||||
}
|
||||
|
||||
val (referencedTypeName, referencedName) = value.substring(1).split('/').takeIf { it.size == 2 } ?: return value
|
||||
val referencedType = ResourceType.getEnum(referencedTypeName) ?: return value
|
||||
return getResourceValue(referencedType, referencedName, referenceConfig)
|
||||
}
|
||||
|
||||
// Converted from com.android.tools.idea.folding.InlinedResource#insertArguments
|
||||
private fun formatArguments(callExpression: UCallExpression, formatString: String): String {
|
||||
if (!formatString.contains('%')) {
|
||||
return formatString
|
||||
}
|
||||
|
||||
val args = callExpression.valueArguments
|
||||
if (args.isEmpty() || !args.first().isPsiValid) {
|
||||
return formatString
|
||||
}
|
||||
|
||||
val matcher = FORMAT.matcher(formatString)
|
||||
var index = 0
|
||||
var prevIndex = 0
|
||||
var nextNumber = 1
|
||||
var start = 0
|
||||
val sb = StringBuilder(2 * formatString.length)
|
||||
while (true) {
|
||||
if (matcher.find(index)) {
|
||||
if ("%" == matcher.group(6)) {
|
||||
index = matcher.end()
|
||||
continue
|
||||
}
|
||||
val matchStart = matcher.start()
|
||||
// Make sure this is not an escaped '%'
|
||||
while (prevIndex < matchStart) {
|
||||
val c = formatString[prevIndex]
|
||||
if (c == '\\') {
|
||||
prevIndex++
|
||||
}
|
||||
prevIndex++
|
||||
}
|
||||
if (prevIndex > matchStart) {
|
||||
// We're in an escape, ignore this result
|
||||
index = prevIndex
|
||||
continue
|
||||
}
|
||||
|
||||
index = matcher.end()
|
||||
|
||||
// Shouldn't throw a number format exception since we've already
|
||||
// matched the pattern in the regexp
|
||||
val number: Int
|
||||
var numberString: String? = matcher.group(1)
|
||||
if (numberString != null) {
|
||||
// Strip off trailing $
|
||||
numberString = numberString.substring(0, numberString.length - 1)
|
||||
number = Integer.parseInt(numberString)
|
||||
nextNumber = number + 1
|
||||
}
|
||||
else {
|
||||
number = nextNumber++
|
||||
}
|
||||
|
||||
if (number > 0 && number < args.size) {
|
||||
val argExpression = args[number]
|
||||
var value: Any? = argExpression.evaluate()
|
||||
|
||||
if (value == null) {
|
||||
value = args[number].asSourceString()
|
||||
}
|
||||
|
||||
for (i in start..matchStart - 1) {
|
||||
sb.append(formatString[i])
|
||||
}
|
||||
|
||||
sb.append("{")
|
||||
sb.append(value)
|
||||
sb.append('}')
|
||||
start = index
|
||||
}
|
||||
}
|
||||
else {
|
||||
var i = start
|
||||
val n = formatString.length
|
||||
while (i < n) {
|
||||
sb.append(formatString[i])
|
||||
i++
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return sb.toString()
|
||||
}
|
||||
|
||||
private fun getAppResources(element: PsiElement): LocalResourceRepository? = ModuleUtilCore.findModuleForPsiElement(element)?.let {
|
||||
AppResourceRepository.getAppResources(it, FORCE_PROJECT_RESOURCE_LOADING)
|
||||
}
|
||||
}
|
||||
+6
-1
@@ -16,6 +16,7 @@
|
||||
|
||||
package org.jetbrains.kotlin.android.inspection
|
||||
|
||||
import com.android.tools.idea.model.AndroidModuleInfo
|
||||
import com.intellij.codeInspection.*
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.psi.PsiElementVisitor
|
||||
@@ -28,7 +29,11 @@ import org.jetbrains.kotlin.psi.psiUtil.addTypeArgument
|
||||
|
||||
class TypeParameterFindViewByIdInspection : AbstractKotlinInspection(), CleanupLocalInspectionTool {
|
||||
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean, session: LocalInspectionToolSession): PsiElementVisitor {
|
||||
val compileSdk = AndroidFacet.getInstance(session.file)?.androidModuleInfo?.buildSdkVersion?.apiLevel
|
||||
val compileSdk = AndroidFacet.getInstance(session.file)
|
||||
?.let { facet -> AndroidModuleInfo.getInstance(facet) }
|
||||
?.buildSdkVersion
|
||||
?.apiLevel
|
||||
|
||||
if (compileSdk == null || compileSdk < 26) {
|
||||
return KtVisitorVoid()
|
||||
}
|
||||
|
||||
+78
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* 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.android.inspection
|
||||
|
||||
import com.intellij.codeInspection.*
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.psi.PsiElementVisitor
|
||||
import org.jetbrains.android.facet.AndroidFacet
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.resolveToCall
|
||||
import org.jetbrains.kotlin.idea.inspections.AbstractKotlinInspection
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.KtPsiUtil.isUnsafeCast
|
||||
import org.jetbrains.kotlin.psi.psiUtil.addTypeArgument
|
||||
|
||||
class TypeParameterFindViewByIdInspection : AbstractKotlinInspection(), CleanupLocalInspectionTool {
|
||||
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean, session: LocalInspectionToolSession): PsiElementVisitor {
|
||||
val compileSdk = AndroidFacet.getInstance(session.file)?.androidModuleInfo?.buildSdkVersion?.apiLevel
|
||||
if (compileSdk == null || compileSdk < 26) {
|
||||
return KtVisitorVoid()
|
||||
}
|
||||
|
||||
return object : KtVisitorVoid() {
|
||||
override fun visitCallExpression(expression: KtCallExpression) {
|
||||
super.visitCallExpression(expression)
|
||||
if (expression.calleeExpression?.text != "findViewById" || expression.typeArguments.isNotEmpty()) {
|
||||
return
|
||||
}
|
||||
|
||||
val parentCast = (expression.parent as? KtBinaryExpressionWithTypeRHS)?.takeIf { isUnsafeCast(it) } ?: return
|
||||
val typeText = parentCast.right?.getTypeTextWithoutQuestionMark() ?: return
|
||||
val callableDescriptor = expression.resolveToCall()?.resultingDescriptor ?: return
|
||||
if (callableDescriptor.name.asString() != "findViewById" || callableDescriptor.typeParameters.size != 1) {
|
||||
return
|
||||
}
|
||||
|
||||
holder.registerProblem(
|
||||
parentCast,
|
||||
"Can be converted to findViewById<$typeText>(...)",
|
||||
ConvertCastToFindViewByIdWithTypeParameter())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ConvertCastToFindViewByIdWithTypeParameter : LocalQuickFix {
|
||||
override fun getFamilyName(): String = "Convert cast to findViewById with type parameter"
|
||||
|
||||
override fun applyFix(project: Project, descriptor: ProblemDescriptor) {
|
||||
val cast = descriptor.psiElement as? KtBinaryExpressionWithTypeRHS ?: return
|
||||
val typeText = cast.right?.getTypeTextWithoutQuestionMark() ?: return
|
||||
val call = cast.left as? KtCallExpression ?: return
|
||||
|
||||
val newCall = call.copy() as KtCallExpression
|
||||
val typeArgument = KtPsiFactory(call).createTypeArgument(typeText)
|
||||
newCall.addTypeArgument(typeArgument)
|
||||
|
||||
cast.replace(newCall)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun KtTypeReference.getTypeTextWithoutQuestionMark(): String? =
|
||||
(typeElement as? KtNullableType)?.innerType?.text ?: typeElement?.text
|
||||
}
|
||||
}
|
||||
+4
-2
@@ -29,6 +29,7 @@ import org.jetbrains.android.dom.resources.Attr;
|
||||
import org.jetbrains.android.dom.resources.DeclareStyleable;
|
||||
import org.jetbrains.android.facet.AndroidFacet;
|
||||
import org.jetbrains.android.resourceManagers.LocalResourceManager;
|
||||
import org.jetbrains.android.resourceManagers.ModuleResourceManagers;
|
||||
import org.jetbrains.android.resourceManagers.ResourceManager;
|
||||
import org.jetbrains.android.util.AndroidResourceUtil;
|
||||
import org.jetbrains.android.util.AndroidUtils;
|
||||
@@ -68,9 +69,10 @@ public class KotlinAndroidGotoDeclarationHandler implements GotoDeclarationHandl
|
||||
collectManifestElements(nestedClassName, fieldName, facet, resourceList);
|
||||
}
|
||||
else {
|
||||
ModuleResourceManagers managers = ModuleResourceManagers.getInstance(facet);
|
||||
ResourceManager manager = info.isSystem()
|
||||
? facet.getSystemResourceManager(false)
|
||||
: facet.getLocalResourceManager();
|
||||
? managers.getSystemResourceManager(false)
|
||||
: managers.getLocalResourceManager();
|
||||
if (manager == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
+147
@@ -0,0 +1,147 @@
|
||||
/*
|
||||
* 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.android.navigation;
|
||||
|
||||
import com.android.resources.ResourceType;
|
||||
import com.intellij.codeInsight.navigation.actions.GotoDeclarationHandler;
|
||||
import com.intellij.openapi.actionSystem.DataContext;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.xml.XmlElement;
|
||||
import org.jetbrains.android.dom.AndroidAttributeValue;
|
||||
import org.jetbrains.android.dom.manifest.Manifest;
|
||||
import org.jetbrains.android.dom.manifest.ManifestElementWithRequiredName;
|
||||
import org.jetbrains.android.dom.resources.Attr;
|
||||
import org.jetbrains.android.dom.resources.DeclareStyleable;
|
||||
import org.jetbrains.android.facet.AndroidFacet;
|
||||
import org.jetbrains.android.resourceManagers.LocalResourceManager;
|
||||
import org.jetbrains.android.resourceManagers.ResourceManager;
|
||||
import org.jetbrains.android.util.AndroidResourceUtil;
|
||||
import org.jetbrains.android.util.AndroidUtils;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.kotlin.android.AndroidUtilKt;
|
||||
import org.jetbrains.kotlin.psi.KtSimpleNameExpression;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
// TODO: ask for extension point
|
||||
// this class is mostly copied from org.jetbrains.android.AndroidGotoDeclarationHandler
|
||||
public class KotlinAndroidGotoDeclarationHandler implements GotoDeclarationHandler {
|
||||
@Override
|
||||
public PsiElement[] getGotoDeclarationTargets(@Nullable PsiElement sourceElement, int offset, Editor editor) {
|
||||
KtSimpleNameExpression referenceExpression = GotoResourceHelperKt.getReferenceExpression(sourceElement);
|
||||
if (referenceExpression == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
AndroidFacet facet = AndroidUtilKt.getAndroidFacetForFile(referenceExpression);
|
||||
if (facet == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
AndroidResourceUtil.MyReferredResourceFieldInfo info = GotoResourceHelperKt.getInfo(referenceExpression, facet);
|
||||
|
||||
if (info == null) return null;
|
||||
|
||||
String nestedClassName = info.getClassName();
|
||||
String fieldName = info.getFieldName();
|
||||
List<PsiElement> resourceList = new ArrayList<PsiElement>();
|
||||
|
||||
if (info.isFromManifest()) {
|
||||
collectManifestElements(nestedClassName, fieldName, facet, resourceList);
|
||||
}
|
||||
else {
|
||||
ResourceManager manager = info.isSystem()
|
||||
? facet.getSystemResourceManager(false)
|
||||
: facet.getLocalResourceManager();
|
||||
if (manager == null) {
|
||||
return null;
|
||||
}
|
||||
manager.collectLazyResourceElements(nestedClassName, fieldName, false, referenceExpression, resourceList);
|
||||
|
||||
if (manager instanceof LocalResourceManager) {
|
||||
LocalResourceManager lrm = (LocalResourceManager) manager;
|
||||
|
||||
if (nestedClassName.equals(ResourceType.ATTR.getName())) {
|
||||
for (Attr attr : lrm.findAttrs(fieldName)) {
|
||||
resourceList.add(attr.getName().getXmlAttributeValue());
|
||||
}
|
||||
}
|
||||
else if (nestedClassName.equals(ResourceType.STYLEABLE.getName())) {
|
||||
for (DeclareStyleable styleable : lrm.findStyleables(fieldName)) {
|
||||
resourceList.add(styleable.getName().getXmlAttributeValue());
|
||||
}
|
||||
|
||||
for (Attr styleable : lrm.findStyleableAttributesByFieldName(fieldName)) {
|
||||
resourceList.add(styleable.getName().getXmlAttributeValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (resourceList.size() > 1) {
|
||||
// Sort to ensure the output is stable, and to prefer the base folders
|
||||
Collections.sort(resourceList, AndroidResourceUtil.RESOURCE_ELEMENT_COMPARATOR);
|
||||
}
|
||||
|
||||
return resourceList.toArray(new PsiElement[resourceList.size()]);
|
||||
}
|
||||
|
||||
private static void collectManifestElements(
|
||||
@NotNull String nestedClassName,
|
||||
@NotNull String fieldName,
|
||||
@NotNull AndroidFacet facet,
|
||||
@NotNull List<PsiElement> result
|
||||
) {
|
||||
Manifest manifest = facet.getManifest();
|
||||
|
||||
if (manifest == null) {
|
||||
return;
|
||||
}
|
||||
List<? extends ManifestElementWithRequiredName> list;
|
||||
|
||||
if ("permission".equals(nestedClassName)) {
|
||||
list = manifest.getPermissions();
|
||||
}
|
||||
else if ("permission_group".equals(nestedClassName)) {
|
||||
list = manifest.getPermissionGroups();
|
||||
}
|
||||
else {
|
||||
return;
|
||||
}
|
||||
for (ManifestElementWithRequiredName domElement : list) {
|
||||
AndroidAttributeValue<String> nameAttribute = domElement.getName();
|
||||
String name = nameAttribute.getValue();
|
||||
|
||||
if (AndroidUtils.equal(name, fieldName, false)) {
|
||||
XmlElement psiElement = nameAttribute.getXmlAttributeValue();
|
||||
|
||||
if (psiElement != null) {
|
||||
result.add(psiElement);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getActionText(DataContext context) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* 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.android.quickfix
|
||||
|
||||
import com.android.SdkConstants
|
||||
import com.android.tools.lint.checks.ApiDetector.REQUIRES_API_ANNOTATION
|
||||
import com.intellij.codeInsight.FileModificationService
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.util.PsiTreeUtil
|
||||
import org.jetbrains.android.inspections.lint.AndroidLintQuickFix
|
||||
import org.jetbrains.android.inspections.lint.AndroidQuickfixContexts
|
||||
import org.jetbrains.android.util.AndroidBundle
|
||||
import org.jetbrains.kotlin.android.hasBackingField
|
||||
import org.jetbrains.kotlin.idea.util.addAnnotation
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
|
||||
|
||||
class AddTargetApiQuickFix(
|
||||
val api: Int,
|
||||
val useRequiresApi: Boolean
|
||||
) : AndroidLintQuickFix {
|
||||
|
||||
private companion object {
|
||||
val FQNAME_TARGET_API = FqName(SdkConstants.FQCN_TARGET_API)
|
||||
val FQNAME_REQUIRES_API = FqName(REQUIRES_API_ANNOTATION)
|
||||
}
|
||||
|
||||
override fun isApplicable(startElement: PsiElement, endElement: PsiElement, contextType: AndroidQuickfixContexts.ContextType): Boolean =
|
||||
getAnnotationContainer(startElement, useRequiresApi) != null
|
||||
|
||||
override fun getName(): String = getAnnotationValue(false).let {
|
||||
if (useRequiresApi) {
|
||||
// Not Available in Android plugin 2.0
|
||||
// AndroidBundle.message("android.lint.fix.add.requires.api", it)
|
||||
"Add @RequiresApi($it) Annotation"
|
||||
} else {
|
||||
AndroidBundle.message("android.lint.fix.add.target.api", it)
|
||||
}
|
||||
}
|
||||
|
||||
override fun apply(startElement: PsiElement, endElement: PsiElement, context: AndroidQuickfixContexts.Context) {
|
||||
val annotationContainer = getAnnotationContainer(startElement, useRequiresApi) ?: return
|
||||
if (!FileModificationService.getInstance().preparePsiElementForWrite(annotationContainer)) {
|
||||
return
|
||||
}
|
||||
|
||||
if (annotationContainer is KtModifierListOwner) {
|
||||
annotationContainer.addAnnotation(
|
||||
if (useRequiresApi) FQNAME_REQUIRES_API else FQNAME_TARGET_API,
|
||||
getAnnotationValue(true),
|
||||
whiteSpaceText = if (annotationContainer.isNewLineNeededForAnnotation()) "\n" else " ")
|
||||
}
|
||||
}
|
||||
|
||||
private fun KtElement.isNewLineNeededForAnnotation() = !(this is KtParameter || this is KtTypeParameter || this is KtPropertyAccessor)
|
||||
|
||||
private fun getAnnotationValue(fullyQualified: Boolean) = getVersionField(api, fullyQualified)
|
||||
|
||||
private fun getAnnotationContainer(element: PsiElement, useRequiresApi: Boolean): PsiElement? {
|
||||
return PsiTreeUtil.findFirstParent(element) {
|
||||
if (useRequiresApi)
|
||||
it.isRequiresApiAnnotationValidTarget()
|
||||
else
|
||||
it.isTargetApiAnnotationValidTarget()
|
||||
}
|
||||
}
|
||||
|
||||
private fun PsiElement.isRequiresApiAnnotationValidTarget(): Boolean {
|
||||
return this is KtClassOrObject ||
|
||||
(this is KtFunction && this !is KtFunctionLiteral) ||
|
||||
(this is KtProperty && !isLocal && hasBackingField()) ||
|
||||
this is KtPropertyAccessor
|
||||
}
|
||||
|
||||
private fun PsiElement.isTargetApiAnnotationValidTarget(): Boolean {
|
||||
return this is KtClassOrObject ||
|
||||
(this is KtFunction && this !is KtFunctionLiteral) ||
|
||||
this is KtPropertyAccessor
|
||||
}
|
||||
}
|
||||
+94
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
* 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.android.quickfix
|
||||
|
||||
import com.intellij.codeInsight.FileModificationService
|
||||
import com.intellij.psi.PsiDocumentManager
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.util.PsiTreeUtil
|
||||
import org.jetbrains.android.inspections.lint.AndroidLintQuickFix
|
||||
import org.jetbrains.android.inspections.lint.AndroidQuickfixContexts
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
||||
import org.jetbrains.kotlin.idea.codeInsight.surroundWith.statement.KotlinIfSurrounder
|
||||
import org.jetbrains.kotlin.idea.core.ShortenReferences
|
||||
import org.jetbrains.kotlin.idea.inspections.findExistingEditor
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
|
||||
|
||||
|
||||
class AddTargetVersionCheckQuickFix(val api: Int) : AndroidLintQuickFix {
|
||||
|
||||
override fun apply(startElement: PsiElement, endElement: PsiElement, context: AndroidQuickfixContexts.Context) {
|
||||
val targetExpression = getTargetExpression(startElement)
|
||||
val project = targetExpression?.project ?: return
|
||||
val editor = targetExpression.findExistingEditor() ?: return
|
||||
|
||||
val file = targetExpression.containingFile
|
||||
val documentManager = PsiDocumentManager.getInstance(project)
|
||||
val document = documentManager.getDocument(file) ?: return
|
||||
|
||||
if (!FileModificationService.getInstance().prepareFileForWrite(file)) {
|
||||
return
|
||||
}
|
||||
|
||||
val surrounder = getSurrounder(targetExpression, "\"VERSION.SDK_INT < ${getVersionField(api, false)}\"")
|
||||
val conditionRange = surrounder.surroundElements(project, editor, arrayOf(targetExpression)) ?: return
|
||||
val conditionText = "android.os.Build.VERSION.SDK_INT >= ${getVersionField(api, true)}"
|
||||
document.replaceString(conditionRange.startOffset, conditionRange.endOffset, conditionText)
|
||||
documentManager.commitDocument(document)
|
||||
|
||||
ShortenReferences.DEFAULT.process(documentManager.getPsiFile(document) as KtFile,
|
||||
conditionRange.startOffset,
|
||||
conditionRange.startOffset + conditionText.length)
|
||||
}
|
||||
|
||||
override fun isApplicable(startElement: PsiElement, endElement: PsiElement, contextType: AndroidQuickfixContexts.ContextType): Boolean =
|
||||
getTargetExpression(startElement) != null
|
||||
|
||||
override fun getName(): String = "Surround with if (VERSION.SDK_INT >= VERSION_CODES.${getVersionField(api, false)}) { ... }"
|
||||
|
||||
private fun getTargetExpression(element: PsiElement): KtElement? {
|
||||
var current = PsiTreeUtil.getParentOfType(element, KtExpression::class.java)
|
||||
while (current != null) {
|
||||
if (current.parent is KtBlockExpression ||
|
||||
current.parent is KtContainerNode ||
|
||||
current.parent is KtWhenEntry ||
|
||||
current.parent is KtFunction ||
|
||||
current.parent is KtPropertyAccessor ||
|
||||
current.parent is KtProperty ||
|
||||
current.parent is KtReturnExpression ||
|
||||
current.parent is KtDestructuringDeclaration) {
|
||||
break
|
||||
}
|
||||
current = PsiTreeUtil.getParentOfType(current, KtExpression::class.java, true)
|
||||
}
|
||||
|
||||
return current
|
||||
}
|
||||
|
||||
private fun getSurrounder(element: KtElement, todoText: String?): KotlinIfSurrounder {
|
||||
val used = element.analyze(BodyResolveMode.PARTIAL)[BindingContext.USED_AS_EXPRESSION, element] ?: false
|
||||
return if (used) {
|
||||
object : KotlinIfSurrounder() {
|
||||
override fun getCodeTemplate(): String = "if (a) { \n} else {\nTODO(${todoText ?: ""})\n}"
|
||||
}
|
||||
} else {
|
||||
KotlinIfSurrounder()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* 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.android.quickfix
|
||||
|
||||
import com.android.sdklib.SdkVersionInfo.*
|
||||
|
||||
|
||||
fun getVersionField(api: Int, fullyQualified: Boolean): String = getBuildCode(api)?.let {
|
||||
if (fullyQualified) "android.os.Build.VERSION_CODES.$it" else it
|
||||
} ?: api.toString()
|
||||
+80
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* 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.android.quickfix
|
||||
|
||||
import com.android.SdkConstants.SUPPORT_ANNOTATIONS_PREFIX
|
||||
import com.android.tools.lint.checks.ApiDetector
|
||||
import com.android.tools.lint.checks.CommentDetector
|
||||
import com.android.tools.lint.checks.ParcelDetector
|
||||
import com.android.tools.lint.detector.api.Issue
|
||||
import com.intellij.psi.JavaPsiFacade
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.search.GlobalSearchScope
|
||||
import org.jetbrains.android.inspections.lint.AndroidLintQuickFix
|
||||
import org.jetbrains.android.inspections.lint.AndroidLintQuickFixProvider
|
||||
import java.util.regex.Pattern
|
||||
|
||||
|
||||
class KotlinAndroidQuickFixProvider : AndroidLintQuickFixProvider {
|
||||
override fun getQuickFixes(
|
||||
issue: Issue,
|
||||
startElement: PsiElement,
|
||||
endElement: PsiElement,
|
||||
message: String,
|
||||
data: Any?
|
||||
): Array<AndroidLintQuickFix> {
|
||||
val fixes: Array<AndroidLintQuickFix> = when (issue) {
|
||||
ApiDetector.UNSUPPORTED, ApiDetector.INLINED -> getApiQuickFixes(issue, startElement, message)
|
||||
ParcelDetector.ISSUE -> arrayOf(ParcelableQuickFix())
|
||||
else -> emptyArray()
|
||||
}
|
||||
|
||||
if (issue != CommentDetector.STOP_SHIP) {
|
||||
return fixes + SuppressLintQuickFix(issue.id)
|
||||
}
|
||||
|
||||
return fixes
|
||||
}
|
||||
|
||||
fun getApiQuickFixes(issue: Issue, element: PsiElement, message: String): Array<AndroidLintQuickFix> {
|
||||
val api = getRequiredVersion(message)
|
||||
if (api == -1) {
|
||||
return AndroidLintQuickFix.EMPTY_ARRAY
|
||||
}
|
||||
|
||||
val project = element.project
|
||||
if (JavaPsiFacade.getInstance(project).findClass(REQUIRES_API_ANNOTATION, GlobalSearchScope.allScope(project)) != null) {
|
||||
return arrayOf(AddTargetApiQuickFix(api, true), AddTargetApiQuickFix(api, false), AddTargetVersionCheckQuickFix(api))
|
||||
}
|
||||
|
||||
return arrayOf(AddTargetApiQuickFix(api, false), AddTargetVersionCheckQuickFix(api))
|
||||
}
|
||||
|
||||
private fun getRequiredVersion(errorMessage: String): Int {
|
||||
val pattern = Pattern.compile("\\s(\\d+)\\s")
|
||||
val matcher = pattern.matcher(errorMessage)
|
||||
if (matcher.find()) {
|
||||
return Integer.parseInt(matcher.group(1))
|
||||
}
|
||||
|
||||
return -1
|
||||
}
|
||||
|
||||
companion object {
|
||||
val REQUIRES_API_ANNOTATION = SUPPORT_ANNOTATIONS_PREFIX + "RequiresApi"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* 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.android.quickfix
|
||||
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.util.PsiTreeUtil
|
||||
import org.jetbrains.android.inspections.lint.AndroidLintQuickFix
|
||||
import org.jetbrains.android.inspections.lint.AndroidQuickfixContexts
|
||||
import org.jetbrains.android.util.AndroidBundle
|
||||
import org.jetbrains.kotlin.android.canAddParcelable
|
||||
import org.jetbrains.kotlin.android.implementParcelable
|
||||
import org.jetbrains.kotlin.android.isParcelize
|
||||
import org.jetbrains.kotlin.psi.KtClass
|
||||
|
||||
|
||||
class ParcelableQuickFix : AndroidLintQuickFix {
|
||||
override fun apply(startElement: PsiElement, endElement: PsiElement, context: AndroidQuickfixContexts.Context) {
|
||||
startElement.getTargetClass()?.implementParcelable()
|
||||
}
|
||||
|
||||
override fun isApplicable(startElement: PsiElement, endElement: PsiElement, contextType: AndroidQuickfixContexts.ContextType): Boolean {
|
||||
val targetClass = startElement.getTargetClass() ?: return false
|
||||
return targetClass.canAddParcelable() && !targetClass.isParcelize()
|
||||
}
|
||||
|
||||
override fun getName(): String = AndroidBundle.message("implement.parcelable.intention.text")
|
||||
|
||||
private fun PsiElement.getTargetClass(): KtClass? = PsiTreeUtil.getParentOfType(this, KtClass::class.java, false)
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* 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.android.quickfix
|
||||
|
||||
import com.android.SdkConstants
|
||||
import com.intellij.codeInsight.FileModificationService
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.util.PsiTreeUtil
|
||||
import org.jetbrains.android.inspections.lint.AndroidLintQuickFix
|
||||
import org.jetbrains.android.inspections.lint.AndroidQuickfixContexts
|
||||
import org.jetbrains.android.util.AndroidBundle
|
||||
import org.jetbrains.kotlin.android.hasBackingField
|
||||
import org.jetbrains.kotlin.idea.util.addAnnotation
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
|
||||
|
||||
class SuppressLintQuickFix(id: String) : AndroidLintQuickFix {
|
||||
private val lintId = getLintId(id)
|
||||
|
||||
override fun apply(startElement: PsiElement, endElement: PsiElement, context: AndroidQuickfixContexts.Context) {
|
||||
val annotationContainer = PsiTreeUtil.findFirstParent(startElement, true) { it.isSuppressLintTarget() } ?: return
|
||||
if (!FileModificationService.getInstance().preparePsiElementForWrite(annotationContainer)) {
|
||||
return
|
||||
}
|
||||
|
||||
val argument = "\"$lintId\""
|
||||
|
||||
when (annotationContainer) {
|
||||
is KtModifierListOwner -> {
|
||||
annotationContainer.addAnnotation(
|
||||
FQNAME_SUPPRESS_LINT,
|
||||
argument,
|
||||
whiteSpaceText = if (annotationContainer.isNewLineNeededForAnnotation()) "\n" else " ",
|
||||
addToExistingAnnotation = { entry -> addArgumentToAnnotation(entry, argument) })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun getName(): String = AndroidBundle.message(SUPPRESS_LINT_MESSAGE, lintId)
|
||||
|
||||
override fun isApplicable(
|
||||
startElement: PsiElement,
|
||||
endElement: PsiElement,
|
||||
contextType: AndroidQuickfixContexts.ContextType
|
||||
): Boolean = true
|
||||
|
||||
private fun addArgumentToAnnotation(entry: KtAnnotationEntry, argument: String): Boolean {
|
||||
// add new arguments to an existing entry
|
||||
val args = entry.valueArgumentList
|
||||
val psiFactory = KtPsiFactory(entry)
|
||||
val newArgList = psiFactory.createCallArguments("($argument)")
|
||||
when {
|
||||
args == null -> // new argument list
|
||||
entry.addAfter(newArgList, entry.lastChild)
|
||||
args.arguments.isEmpty() -> // replace '()' with a new argument list
|
||||
args.replace(newArgList)
|
||||
args.arguments.none { it.textMatches(argument) } ->
|
||||
args.addArgument(newArgList.arguments[0])
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
private fun getLintId(intentionId: String) =
|
||||
if (intentionId.startsWith(INTENTION_NAME_PREFIX)) intentionId.substring(INTENTION_NAME_PREFIX.length) else intentionId
|
||||
|
||||
private fun KtElement.isNewLineNeededForAnnotation(): Boolean {
|
||||
return !(this is KtParameter ||
|
||||
this is KtTypeParameter ||
|
||||
this is KtPropertyAccessor)
|
||||
}
|
||||
|
||||
private fun PsiElement.isSuppressLintTarget(): Boolean {
|
||||
return this is KtDeclaration &&
|
||||
(this as? KtProperty)?.hasBackingField() ?: true &&
|
||||
this !is KtFunctionLiteral &&
|
||||
this !is KtDestructuringDeclaration
|
||||
}
|
||||
private companion object {
|
||||
val INTENTION_NAME_PREFIX = "AndroidLint"
|
||||
val SUPPRESS_LINT_MESSAGE = "android.lint.fix.suppress.lint.api.annotation"
|
||||
val FQNAME_SUPPRESS_LINT = FqName(SdkConstants.FQCN_SUPPRESS_LINT)
|
||||
}
|
||||
}
|
||||
@@ -19,7 +19,7 @@ package org.jetbrains.kotlin.android.lint
|
||||
import com.intellij.codeInspection.InspectionProfileEntry
|
||||
import com.intellij.testFramework.fixtures.impl.CodeInsightTestFixtureImpl
|
||||
import com.intellij.util.PathUtil
|
||||
import org.jetbrains.android.inspections.klint.AndroidLintInspectionBase
|
||||
import org.jetbrains.android.inspections.lint.AndroidLintInspectionBase
|
||||
import org.jetbrains.kotlin.android.KotlinAndroidTestCase
|
||||
import org.jetbrains.kotlin.idea.test.ConfigLibraryUtil
|
||||
import org.jetbrains.kotlin.test.InTextDirectivesUtils
|
||||
|
||||
+84
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* 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.android.lint
|
||||
|
||||
import com.intellij.codeInspection.InspectionProfileEntry
|
||||
import com.intellij.testFramework.fixtures.impl.CodeInsightTestFixtureImpl
|
||||
import com.intellij.util.PathUtil
|
||||
import org.jetbrains.android.inspections.klint.AndroidLintInspectionBase
|
||||
import org.jetbrains.kotlin.android.KotlinAndroidTestCase
|
||||
import org.jetbrains.kotlin.idea.test.ConfigLibraryUtil
|
||||
import org.jetbrains.kotlin.test.InTextDirectivesUtils
|
||||
import org.jetbrains.kotlin.test.InTextDirectivesUtils.findStringWithPrefixes
|
||||
import java.io.File
|
||||
|
||||
abstract class AbstractKotlinLintTest : KotlinAndroidTestCase() {
|
||||
|
||||
override fun setUp() {
|
||||
super.setUp()
|
||||
AndroidLintInspectionBase.invalidateInspectionShortName2IssueMap()
|
||||
(myFixture as CodeInsightTestFixtureImpl).setVirtualFileFilter { false } // Allow access to tree elements.
|
||||
ConfigLibraryUtil.configureKotlinRuntime(myModule)
|
||||
ConfigLibraryUtil.addLibrary(myModule, "androidExtensionsRuntime", "dist/kotlinc/lib", arrayOf("android-extensions-runtime.jar"))
|
||||
}
|
||||
|
||||
override fun tearDown() {
|
||||
ConfigLibraryUtil.unConfigureKotlinRuntime(myModule)
|
||||
ConfigLibraryUtil.removeLibrary(myModule, "androidExtensionsRuntime")
|
||||
super.tearDown()
|
||||
}
|
||||
|
||||
fun doTest(path: String) {
|
||||
val ktFile = File(path)
|
||||
val fileText = ktFile.readText()
|
||||
val mainInspectionClassName = findStringWithPrefixes(fileText, "// INSPECTION_CLASS: ") ?: error("Empty class name")
|
||||
val dependencies = InTextDirectivesUtils.findLinesWithPrefixesRemoved(fileText, "// DEPENDENCY: ")
|
||||
|
||||
val inspectionClassNames = mutableListOf(mainInspectionClassName)
|
||||
for (i in 2..100) {
|
||||
val className = findStringWithPrefixes(ktFile.readText(), "// INSPECTION_CLASS$i: ") ?: break
|
||||
inspectionClassNames += className
|
||||
}
|
||||
|
||||
myFixture.enableInspections(*inspectionClassNames.map { className ->
|
||||
val inspectionClass = Class.forName(className)
|
||||
inspectionClass.newInstance() as InspectionProfileEntry
|
||||
}.toTypedArray())
|
||||
|
||||
val additionalResourcesDir = File(ktFile.parentFile, getTestName(true))
|
||||
if (additionalResourcesDir.exists()) {
|
||||
for (file in additionalResourcesDir.listFiles()) {
|
||||
if (file.isFile) {
|
||||
myFixture.copyFileToProject(file.absolutePath, file.name)
|
||||
}
|
||||
else if (file.isDirectory) {
|
||||
myFixture.copyDirectoryToProject(file.absolutePath, file.name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val virtualFile = myFixture.copyFileToProject(ktFile.absolutePath, "src/${PathUtil.getFileName(path)}")
|
||||
myFixture.configureFromExistingVirtualFile(virtualFile)
|
||||
|
||||
dependencies.forEach { dependency ->
|
||||
val (dependencyFile, dependencyTargetPath) = dependency.split(" -> ").map(String::trim)
|
||||
myFixture.copyFileToProject("${PathUtil.getParentPath(path)}/$dependencyFile", "src/$dependencyTargetPath")
|
||||
}
|
||||
|
||||
myFixture.checkHighlighting(true, false, true)
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -19,7 +19,7 @@ package org.jetbrains.kotlin.android.quickfix
|
||||
import com.intellij.codeInspection.InspectionProfileEntry
|
||||
import com.intellij.openapi.util.io.FileUtil
|
||||
import com.intellij.util.PathUtil
|
||||
import org.jetbrains.android.inspections.klint.AndroidLintInspectionBase
|
||||
import org.jetbrains.android.inspections.lint.AndroidLintInspectionBase
|
||||
import org.jetbrains.kotlin.android.KotlinAndroidTestCase
|
||||
import org.jetbrains.kotlin.test.InTextDirectivesUtils
|
||||
import java.io.File
|
||||
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* 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.android.quickfix
|
||||
|
||||
import com.intellij.codeInspection.InspectionProfileEntry
|
||||
import com.intellij.openapi.util.io.FileUtil
|
||||
import com.intellij.util.PathUtil
|
||||
import org.jetbrains.android.inspections.klint.AndroidLintInspectionBase
|
||||
import org.jetbrains.kotlin.android.KotlinAndroidTestCase
|
||||
import org.jetbrains.kotlin.test.InTextDirectivesUtils
|
||||
import java.io.File
|
||||
|
||||
|
||||
abstract class AbstractAndroidLintQuickfixTest : KotlinAndroidTestCase() {
|
||||
|
||||
override fun setUp() {
|
||||
AndroidLintInspectionBase.invalidateInspectionShortName2IssueMap()
|
||||
super.setUp()
|
||||
}
|
||||
|
||||
fun doTest(path: String) {
|
||||
val fileText = FileUtil.loadFile(File(path), true)
|
||||
val intentionText = InTextDirectivesUtils.findStringWithPrefixes(fileText, "// INTENTION_TEXT: ") ?: error("Empty intention text")
|
||||
val mainInspectionClassName = InTextDirectivesUtils.findStringWithPrefixes(fileText, "// INSPECTION_CLASS: ") ?: error("Empty inspection class name")
|
||||
val dependency = InTextDirectivesUtils.findStringWithPrefixes(fileText, "// DEPENDENCY: ")
|
||||
val intentionAvailable = !InTextDirectivesUtils.isDirectiveDefined(fileText, "// INTENTION_NOT_AVAILABLE")
|
||||
|
||||
val inspection = Class.forName(mainInspectionClassName).newInstance() as InspectionProfileEntry
|
||||
myFixture.enableInspections(inspection)
|
||||
|
||||
val sourceFile = myFixture.copyFileToProject(path, "src/${PathUtil.getFileName(path)}")
|
||||
myFixture.configureFromExistingVirtualFile(sourceFile)
|
||||
|
||||
if (dependency != null) {
|
||||
val (dependencyFile, dependencyTargetPath) = dependency.split(" -> ").map(String::trim)
|
||||
myFixture.copyFileToProject("${PathUtil.getParentPath(path)}/$dependencyFile", "src/$dependencyTargetPath")
|
||||
}
|
||||
|
||||
if (intentionAvailable) {
|
||||
val intention = myFixture.getAvailableIntention(intentionText) ?: error("Failed to find intention")
|
||||
myFixture.launchAction(intention)
|
||||
myFixture.checkResultByFile(path + ".expected")
|
||||
}
|
||||
else {
|
||||
assertNull("Intention should not be available", myFixture.availableIntentions.find { it.text == intentionText })
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -46,6 +46,7 @@ dependencies {
|
||||
testRuntime(intellijPluginDep("coverage"))
|
||||
testRuntime(intellijPluginDep("maven"))
|
||||
testRuntime(intellijPluginDep("android"))
|
||||
testRuntime(intellijPluginDep("smali"))
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
plugins {
|
||||
kotlin("jvm")
|
||||
id("jps-compatible")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
testRuntime(intellijDep())
|
||||
|
||||
compileOnly(project(":idea"))
|
||||
compileOnly(project(":idea:idea-jvm"))
|
||||
compile(project(":idea:kotlin-gradle-tooling"))
|
||||
|
||||
compile(project(":compiler:frontend"))
|
||||
compile(project(":compiler:frontend.java"))
|
||||
compile(project(":compiler:frontend.script"))
|
||||
|
||||
compile(project(":js:js.frontend"))
|
||||
|
||||
compileOnly(intellijDep())
|
||||
compileOnly(intellijPluginDep("gradle"))
|
||||
compileOnly(intellijPluginDep("Groovy"))
|
||||
compileOnly(intellijPluginDep("junit"))
|
||||
|
||||
testCompile(projectTests(":idea"))
|
||||
testCompile(projectTests(":idea:idea-test-framework"))
|
||||
|
||||
testCompile(intellijPluginDep("gradle"))
|
||||
testCompileOnly(intellijPluginDep("Groovy"))
|
||||
testCompileOnly(intellijDep())
|
||||
|
||||
testRuntime(projectDist(":kotlin-reflect"))
|
||||
testRuntime(project(":idea:idea-jvm"))
|
||||
testRuntime(project(":idea:idea-android"))
|
||||
testRuntime(project(":plugins:kapt3-idea"))
|
||||
testRuntime(project(":plugins:android-extensions-ide"))
|
||||
testRuntime(project(":plugins:lint"))
|
||||
testRuntime(project(":sam-with-receiver-ide-plugin"))
|
||||
testRuntime(project(":allopen-ide-plugin"))
|
||||
testRuntime(project(":noarg-ide-plugin"))
|
||||
// TODO: the order of the plugins matters here, consider avoiding order-dependency
|
||||
testRuntime(intellijPluginDep("junit"))
|
||||
testRuntime(intellijPluginDep("testng"))
|
||||
testRuntime(intellijPluginDep("properties"))
|
||||
testRuntime(intellijPluginDep("gradle"))
|
||||
testRuntime(intellijPluginDep("Groovy"))
|
||||
testRuntime(intellijPluginDep("coverage"))
|
||||
testRuntime(intellijPluginDep("maven"))
|
||||
testRuntime(intellijPluginDep("android"))
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
"main" {
|
||||
projectDefault()
|
||||
resources.srcDir("res").apply { include("**") }
|
||||
}
|
||||
"test" { projectDefault() }
|
||||
}
|
||||
|
||||
testsJar()
|
||||
|
||||
projectTest {
|
||||
workingDir = rootDir
|
||||
useAndroidSdk()
|
||||
}
|
||||
|
||||
configureInstrumentation()
|
||||
+1
-1
@@ -41,7 +41,7 @@ object ThreadTrackerPatcherForTeamCityTesting {
|
||||
|
||||
patched.compareAndSet(false, true)
|
||||
|
||||
IdeaForkJoinWorkerThreadFactory.setupForkJoinCommonPool()
|
||||
IdeaForkJoinWorkerThreadFactory.setupForkJoinCommonPool(true)
|
||||
|
||||
// Check setup was successful and patching isn't needed
|
||||
val commonPoolFactoryName = ForkJoinPool.commonPool().factory::class.java.name
|
||||
|
||||
+77
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
import com.intellij.concurrency.IdeaForkJoinWorkerThreadFactory
|
||||
import com.intellij.testFramework.ThreadTracker
|
||||
import java.lang.reflect.Modifier
|
||||
import java.util.concurrent.ForkJoinPool
|
||||
import java.util.concurrent.atomic.AtomicBoolean
|
||||
|
||||
/*
|
||||
Workaround for ThreadTracker.checkLeak() failures on TeamCity.
|
||||
|
||||
Currently TeamCity runs tests in classloader without boot.jar where IdeaForkJoinWorkerThreadFactory is defined. That
|
||||
makes ForkJoinPool.commonPool() silently ignores java.util.concurrent.ForkJoinPool.common.threadFactory
|
||||
(because of java.lang.ClassNotFoundException) option during factory initialization.
|
||||
|
||||
Standard names for ForkJoinPool threads doesn't pass ThreadTracker.checkLeak() check and that ruins tests constantly.
|
||||
As it's allowed to reorder tests on TeamCity and any test can be first at some point, this patch should be applied at
|
||||
some common place.
|
||||
*/
|
||||
object ThreadTrackerPatcherForTeamCityTesting {
|
||||
private val patched = AtomicBoolean(false)
|
||||
|
||||
fun patchThreadTracker() {
|
||||
if (patched.get()) return
|
||||
|
||||
patched.compareAndSet(false, true)
|
||||
|
||||
IdeaForkJoinWorkerThreadFactory.setupForkJoinCommonPool()
|
||||
|
||||
// Check setup was successful and patching isn't needed
|
||||
val commonPoolFactoryName = ForkJoinPool.commonPool().factory::class.java.name
|
||||
if (commonPoolFactoryName == IdeaForkJoinWorkerThreadFactory::class.java.name) {
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
val wellKnownOffendersField = try {
|
||||
ThreadTracker::class.java.getDeclaredField("wellKnownOffenders")
|
||||
}
|
||||
catch (communityPropertyNotFoundEx: NoSuchFieldException) {
|
||||
ThreadTracker::class.java.declaredFields.single {
|
||||
Modifier.isStatic(it.modifiers) && MutableSet::class.java.isAssignableFrom(it.type)
|
||||
}
|
||||
}
|
||||
|
||||
wellKnownOffendersField.isAccessible = true
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
val wellKnownOffenders = wellKnownOffendersField.get(null) as MutableSet<String>
|
||||
|
||||
wellKnownOffenders.add("ForkJoinPool.commonPool-worker-")
|
||||
println("Patching ThreadTracker was successful")
|
||||
}
|
||||
catch (e: NoSuchFieldException) {
|
||||
println("Patching ThreadTracker failed: " + e)
|
||||
}
|
||||
catch (e: IllegalAccessException) {
|
||||
println("Patching ThreadTracker failed: " + e)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -50,7 +50,7 @@ class KotlinDebuggerSettings : XDebuggerSettings<KotlinDebuggerSettings>("kotlin
|
||||
override fun getState() = this
|
||||
override fun get() = this
|
||||
|
||||
override fun loadState(state: KotlinDebuggerSettings?) {
|
||||
if (state != null) XmlSerializerUtil.copyBean<KotlinDebuggerSettings>(state, this)
|
||||
override fun loadState(state: KotlinDebuggerSettings) {
|
||||
XmlSerializerUtil.copyBean<KotlinDebuggerSettings>(state, this)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* 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.debugger
|
||||
|
||||
|
||||
import com.intellij.openapi.components.State
|
||||
import com.intellij.openapi.components.Storage
|
||||
import com.intellij.openapi.options.Configurable
|
||||
import com.intellij.openapi.options.SimpleConfigurable
|
||||
import com.intellij.openapi.util.Getter
|
||||
import com.intellij.util.xmlb.XmlSerializerUtil
|
||||
import com.intellij.xdebugger.XDebuggerUtil
|
||||
import com.intellij.xdebugger.settings.DebuggerSettingsCategory
|
||||
import com.intellij.xdebugger.settings.XDebuggerSettings
|
||||
import org.jetbrains.kotlin.idea.debugger.stepping.KotlinSteppingConfigurableUi
|
||||
|
||||
@State(name = "KotlinDebuggerSettings", storages = arrayOf(Storage("kotlin_debug.xml")))
|
||||
class KotlinDebuggerSettings : XDebuggerSettings<KotlinDebuggerSettings>("kotlin_debugger"), Getter<KotlinDebuggerSettings> {
|
||||
var DEBUG_RENDER_DELEGATED_PROPERTIES: Boolean = true
|
||||
var DEBUG_DISABLE_KOTLIN_INTERNAL_CLASSES: Boolean = true
|
||||
var DEBUG_IS_FILTER_FOR_STDLIB_ALREADY_ADDED: Boolean = false
|
||||
|
||||
companion object {
|
||||
fun getInstance(): KotlinDebuggerSettings {
|
||||
return XDebuggerUtil.getInstance()?.getDebuggerSettings(KotlinDebuggerSettings::class.java)!!
|
||||
}
|
||||
}
|
||||
|
||||
override fun createConfigurables(category: DebuggerSettingsCategory): Collection<Configurable?> {
|
||||
return when (category) {
|
||||
DebuggerSettingsCategory.STEPPING ->
|
||||
listOf(SimpleConfigurable.create(
|
||||
"reference.idesettings.debugger.kotlin.stepping",
|
||||
"Kotlin",
|
||||
KotlinSteppingConfigurableUi::class.java,
|
||||
this))
|
||||
DebuggerSettingsCategory.DATA_VIEWS ->
|
||||
listOf(SimpleConfigurable.create(
|
||||
"reference.idesettings.debugger.kotlin.data.view",
|
||||
"Kotlin",
|
||||
KotlinDelegatedPropertyRendererConfigurableUi::class.java,
|
||||
this))
|
||||
else -> listOf()
|
||||
}
|
||||
}
|
||||
|
||||
override fun getState() = this
|
||||
override fun get() = this
|
||||
|
||||
override fun loadState(state: KotlinDebuggerSettings?) {
|
||||
if (state != null) XmlSerializerUtil.copyBean<KotlinDebuggerSettings>(state, this)
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -93,7 +93,7 @@ class KotlinCodeFragmentFactory : CodeFragmentFactory() {
|
||||
semaphore.down()
|
||||
val nameRef = AtomicReference<KotlinType>()
|
||||
val worker = object : KotlinRuntimeTypeEvaluator(
|
||||
null, expression, debuggerContext, ProgressManager.getInstance().progressIndicator
|
||||
null, expression, debuggerContext, ProgressManager.getInstance().progressIndicator!!
|
||||
) {
|
||||
override fun typeCalculationFinished(type: KotlinType?) {
|
||||
nameRef.set(type)
|
||||
|
||||
+434
@@ -0,0 +1,434 @@
|
||||
/*
|
||||
* 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.evaluate
|
||||
|
||||
import com.intellij.debugger.DebuggerManagerEx
|
||||
import com.intellij.debugger.engine.evaluation.CodeFragmentFactory
|
||||
import com.intellij.debugger.engine.evaluation.CodeFragmentKind
|
||||
import com.intellij.debugger.engine.evaluation.TextWithImports
|
||||
import com.intellij.debugger.engine.events.DebuggerCommandImpl
|
||||
import com.intellij.debugger.impl.DebuggerContextImpl
|
||||
import com.intellij.debugger.ui.impl.watch.NodeDescriptorImpl
|
||||
import com.intellij.ide.highlighter.JavaFileType
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.diagnostic.Logger
|
||||
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.search.GlobalSearchScope
|
||||
import com.intellij.psi.util.PsiTreeUtil
|
||||
import com.intellij.psi.util.PsiTypesUtil
|
||||
import com.intellij.util.IncorrectOperationException
|
||||
import com.intellij.util.concurrency.Semaphore
|
||||
import com.intellij.xdebugger.XDebuggerManager
|
||||
import com.intellij.xdebugger.impl.XDebugSessionImpl
|
||||
import com.intellij.xdebugger.impl.ui.tree.ValueMarkup
|
||||
import com.sun.jdi.*
|
||||
import org.jetbrains.annotations.TestOnly
|
||||
import org.jetbrains.eval4j.jdi.asValue
|
||||
import org.jetbrains.kotlin.asJava.classes.KtLightClass
|
||||
import org.jetbrains.kotlin.idea.KotlinFileType
|
||||
import org.jetbrains.kotlin.idea.codeInsight.CodeInsightUtils
|
||||
import org.jetbrains.kotlin.idea.debugger.KotlinEditorTextProvider
|
||||
import org.jetbrains.kotlin.idea.j2k.J2kPostProcessor
|
||||
import org.jetbrains.kotlin.idea.refactoring.j2k
|
||||
import org.jetbrains.kotlin.idea.refactoring.j2kText
|
||||
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers
|
||||
import org.jetbrains.kotlin.idea.util.application.executeWriteCommand
|
||||
import org.jetbrains.kotlin.idea.versions.getKotlinJvmRuntimeMarkerClass
|
||||
import org.jetbrains.kotlin.j2k.AfterConversionPass
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.getElementTextWithContext
|
||||
import org.jetbrains.kotlin.psi.psiUtil.quoteIfNeeded
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.typeUtil.makeNullable
|
||||
import java.util.concurrent.atomic.AtomicReference
|
||||
|
||||
class KotlinCodeFragmentFactory : CodeFragmentFactory() {
|
||||
override fun createCodeFragment(item: TextWithImports, context: PsiElement?, project: Project): JavaCodeFragment {
|
||||
val contextElement = getWrappedContextElement(project, context)
|
||||
if (contextElement == null) {
|
||||
LOG.warn("CodeFragment with null context created:\noriginalContext = ${context?.getElementTextWithContext()}")
|
||||
}
|
||||
val codeFragment = if (item.kind == CodeFragmentKind.EXPRESSION) {
|
||||
KtExpressionCodeFragment(
|
||||
project,
|
||||
"fragment.kt",
|
||||
item.text,
|
||||
initImports(item.imports),
|
||||
contextElement
|
||||
)
|
||||
} else {
|
||||
KtBlockCodeFragment(
|
||||
project,
|
||||
"fragment.kt",
|
||||
item.text,
|
||||
initImports(item.imports),
|
||||
contextElement
|
||||
)
|
||||
}
|
||||
|
||||
codeFragment.putCopyableUserData(KtCodeFragment.RUNTIME_TYPE_EVALUATOR, { expression: KtExpression ->
|
||||
val debuggerContext = DebuggerManagerEx.getInstanceEx(project).context
|
||||
val debuggerSession = debuggerContext.debuggerSession
|
||||
if (debuggerSession == null || debuggerContext.suspendContext == null) {
|
||||
null
|
||||
} else {
|
||||
val semaphore = Semaphore()
|
||||
semaphore.down()
|
||||
val nameRef = AtomicReference<KotlinType>()
|
||||
val worker = object : KotlinRuntimeTypeEvaluator(
|
||||
null, expression, debuggerContext, ProgressManager.getInstance().progressIndicator
|
||||
) {
|
||||
override fun typeCalculationFinished(type: KotlinType?) {
|
||||
nameRef.set(type)
|
||||
semaphore.up()
|
||||
}
|
||||
}
|
||||
|
||||
debuggerContext.debugProcess?.managerThread?.invoke(worker)
|
||||
|
||||
for (i in 0..50) {
|
||||
ProgressManager.checkCanceled()
|
||||
if (semaphore.waitFor(20)) break
|
||||
}
|
||||
|
||||
nameRef.get()
|
||||
}
|
||||
})
|
||||
|
||||
if (contextElement != null && contextElement !is KtElement) {
|
||||
codeFragment.putCopyableUserData(KtCodeFragment.FAKE_CONTEXT_FOR_JAVA_FILE, {
|
||||
val emptyFile = createFakeFileWithJavaContextElement("", contextElement)
|
||||
|
||||
val debuggerContext = DebuggerManagerEx.getInstanceEx(project).context
|
||||
val debuggerSession = debuggerContext.debuggerSession
|
||||
if ((debuggerSession == null || debuggerContext.suspendContext == null) && !ApplicationManager.getApplication().isUnitTestMode) {
|
||||
LOG.warn("Couldn't create fake context element for java file, debugger isn't paused on breakpoint")
|
||||
return@putCopyableUserData emptyFile
|
||||
}
|
||||
|
||||
val frameDescriptor = getFrameInfo(contextElement, debuggerContext)
|
||||
if (frameDescriptor == null) {
|
||||
LOG.warn("Couldn't get info about 'this' and local variables for ${debuggerContext.sourcePosition.file.name}:${debuggerContext.sourcePosition.line}")
|
||||
return@putCopyableUserData emptyFile
|
||||
}
|
||||
|
||||
val receiverTypeReference =
|
||||
frameDescriptor.thisObject?.let { createKotlinProperty(project, "this_0", it.type().name(), it) }?.typeReference
|
||||
val receiverTypeText = receiverTypeReference?.let { "${it.text}." } ?: ""
|
||||
|
||||
val kotlinVariablesText =
|
||||
frameDescriptor.visibleVariables.entries.associate { it.key.name() to it.value }.kotlinVariablesAsText(project)
|
||||
|
||||
val fakeFunctionText = "fun ${receiverTypeText}_java_locals_debug_fun_() {\n$kotlinVariablesText\n}"
|
||||
|
||||
val fakeFile = createFakeFileWithJavaContextElement(fakeFunctionText, contextElement)
|
||||
val fakeFunction = fakeFile.declarations.firstOrNull() as? KtFunction
|
||||
val fakeContext = (fakeFunction?.bodyExpression as? KtBlockExpression)?.statements?.lastOrNull()
|
||||
|
||||
return@putCopyableUserData wrapContextIfNeeded(project, contextElement, fakeContext) ?: emptyFile
|
||||
})
|
||||
}
|
||||
|
||||
return codeFragment
|
||||
}
|
||||
|
||||
private fun getFrameInfo(contextElement: PsiElement?, debuggerContext: DebuggerContextImpl): FrameInfo? {
|
||||
val semaphore = Semaphore()
|
||||
semaphore.down()
|
||||
|
||||
var frameInfo: FrameInfo? = null
|
||||
|
||||
val worker = object : DebuggerCommandImpl() {
|
||||
override fun action() {
|
||||
try {
|
||||
val frame = if (ApplicationManager.getApplication().isUnitTestMode)
|
||||
contextElement?.getCopyableUserData(DEBUG_CONTEXT_FOR_TESTS)?.frameProxy?.stackFrame
|
||||
else
|
||||
debuggerContext.frameProxy?.stackFrame
|
||||
|
||||
val visibleVariables = frame?.let {
|
||||
val values = it.getValues(it.visibleVariables())
|
||||
values.filterValues { it != null }
|
||||
} ?: emptyMap()
|
||||
|
||||
frameInfo = FrameInfo(frame?.thisObject(), visibleVariables)
|
||||
} catch (ignored: AbsentInformationException) {
|
||||
// Debug info unavailable
|
||||
} finally {
|
||||
semaphore.up()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
debuggerContext.debugProcess?.managerThread?.invoke(worker)
|
||||
|
||||
for (i in 0..50) {
|
||||
if (semaphore.waitFor(20)) break
|
||||
}
|
||||
|
||||
return frameInfo
|
||||
}
|
||||
|
||||
private class FrameInfo(val thisObject: Value?, val visibleVariables: Map<LocalVariable, Value>)
|
||||
|
||||
private fun initImports(imports: String?): String? {
|
||||
if (imports != null && !imports.isEmpty()) {
|
||||
return imports.split(KtCodeFragment.IMPORT_SEPARATOR)
|
||||
.mapNotNull { fixImportIfNeeded(it) }
|
||||
.joinToString(KtCodeFragment.IMPORT_SEPARATOR)
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
private fun fixImportIfNeeded(import: String): String? {
|
||||
// skip arrays
|
||||
if (import.endsWith("[]")) {
|
||||
return fixImportIfNeeded(import.removeSuffix("[]").trim())
|
||||
}
|
||||
|
||||
// skip primitive types
|
||||
if (PsiTypesUtil.boxIfPossible(import) != import) {
|
||||
return null
|
||||
}
|
||||
return import
|
||||
}
|
||||
|
||||
private fun getWrappedContextElement(project: Project, context: PsiElement?): PsiElement? {
|
||||
val newContext = getContextElement(context)
|
||||
if (newContext !is KtElement) return newContext
|
||||
return wrapContextIfNeeded(project, context, newContext)
|
||||
}
|
||||
|
||||
override fun createPresentationCodeFragment(item: TextWithImports, context: PsiElement?, project: Project): JavaCodeFragment {
|
||||
val kotlinCodeFragment = createCodeFragment(item, context, project)
|
||||
if (PsiTreeUtil.hasErrorElements(kotlinCodeFragment) && kotlinCodeFragment is KtExpressionCodeFragment) {
|
||||
val javaExpression = try {
|
||||
PsiElementFactory.SERVICE.getInstance(project).createExpressionFromText(item.text, context)
|
||||
} catch (e: IncorrectOperationException) {
|
||||
null
|
||||
}
|
||||
|
||||
val importList = try {
|
||||
kotlinCodeFragment.importsAsImportList()?.let {
|
||||
(PsiFileFactory.getInstance(project).createFileFromText(
|
||||
"dummy.java", JavaFileType.INSTANCE, it.text
|
||||
) as? PsiJavaFile)?.importList
|
||||
}
|
||||
} catch (e: IncorrectOperationException) {
|
||||
null
|
||||
}
|
||||
|
||||
if (javaExpression != null && !PsiTreeUtil.hasErrorElements(javaExpression)) {
|
||||
var convertedFragment: KtExpressionCodeFragment? = null
|
||||
project.executeWriteCommand("Convert java expression to kotlin in Evaluate Expression") {
|
||||
try {
|
||||
val newText = javaExpression.j2kText()
|
||||
val newImports = importList?.j2kText()
|
||||
if (newText != null) {
|
||||
convertedFragment = KtExpressionCodeFragment(
|
||||
project,
|
||||
kotlinCodeFragment.name,
|
||||
newText,
|
||||
newImports,
|
||||
kotlinCodeFragment.context
|
||||
)
|
||||
|
||||
AfterConversionPass(project, J2kPostProcessor(formatCode = false)).run(convertedFragment!!, range = null)
|
||||
}
|
||||
} catch (e: Throwable) {
|
||||
// ignored because text can be invalid
|
||||
LOG.error("Couldn't convert expression:\n`${javaExpression.text}`", e)
|
||||
}
|
||||
}
|
||||
return convertedFragment ?: kotlinCodeFragment
|
||||
}
|
||||
}
|
||||
return kotlinCodeFragment
|
||||
}
|
||||
|
||||
override fun isContextAccepted(contextElement: PsiElement?): Boolean {
|
||||
return when {
|
||||
// PsiCodeBlock -> DummyHolder -> originalElement
|
||||
contextElement is PsiCodeBlock -> isContextAccepted(contextElement.context?.context)
|
||||
contextElement == null -> false
|
||||
contextElement.language == KotlinFileType.INSTANCE.language -> true
|
||||
contextElement.language == JavaFileType.INSTANCE.language -> {
|
||||
getKotlinJvmRuntimeMarkerClass(contextElement.project, contextElement.resolveScope) != null
|
||||
}
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
|
||||
override fun getFileType(): KotlinFileType = KotlinFileType.INSTANCE
|
||||
|
||||
override fun getEvaluatorBuilder() = KotlinEvaluationBuilder
|
||||
|
||||
companion object {
|
||||
private val LOG = Logger.getInstance(this::class.java)
|
||||
|
||||
val LABEL_VARIABLE_VALUE_KEY: Key<Value> = Key.create<Value>("_label_variable_value_key_")
|
||||
|
||||
private const val DEBUG_LABEL_SUFFIX: String = "_DebugLabel"
|
||||
|
||||
@TestOnly
|
||||
val DEBUG_CONTEXT_FOR_TESTS: Key<DebuggerContextImpl> = Key.create("DEBUG_CONTEXT_FOR_TESTS")
|
||||
|
||||
fun getContextElement(elementAt: PsiElement?): PsiElement? {
|
||||
if (elementAt == null) return null
|
||||
|
||||
if (elementAt is PsiCodeBlock) {
|
||||
return getContextElement(elementAt.context?.context)
|
||||
}
|
||||
|
||||
if (elementAt is KtLightClass) {
|
||||
return getContextElement(elementAt.kotlinOrigin)
|
||||
}
|
||||
|
||||
val containingFile = elementAt.containingFile
|
||||
if (containingFile is PsiJavaFile) return elementAt
|
||||
if (containingFile !is KtFile) return null
|
||||
|
||||
// elementAt can be PsiWhiteSpace when codeFragment is created from line start offset (in case of first opening EE window)
|
||||
val lineStartOffset = if (elementAt is PsiWhiteSpace || elementAt is PsiComment) {
|
||||
PsiTreeUtil.skipSiblingsForward(elementAt, PsiWhiteSpace::class.java, PsiComment::class.java)?.textOffset
|
||||
?: elementAt.textOffset
|
||||
} else {
|
||||
elementAt.textOffset
|
||||
}
|
||||
|
||||
fun KtElement.takeIfAcceptedAsCodeFragmentContext() = takeIf { KotlinEditorTextProvider.isAcceptedAsCodeFragmentContext(it) }
|
||||
|
||||
PsiTreeUtil.findElementOfClassAtOffset(containingFile, lineStartOffset, KtExpression::class.java, false)
|
||||
?.takeIfAcceptedAsCodeFragmentContext()
|
||||
?.let { return CodeInsightUtils.getTopmostElementAtOffset(it, lineStartOffset, KtExpression::class.java) }
|
||||
|
||||
KotlinEditorTextProvider.findExpressionInner(elementAt, true)
|
||||
?.takeIfAcceptedAsCodeFragmentContext()
|
||||
?.let { return it }
|
||||
|
||||
return containingFile
|
||||
}
|
||||
|
||||
//internal for tests
|
||||
fun createCodeFragmentForLabeledObjects(project: Project, markupMap: Map<*, ValueMarkup>): Pair<String, Map<String, Value>> {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
val variables = markupMap.entries.associate {
|
||||
val (value, markup) = it
|
||||
"${markup.text}$DEBUG_LABEL_SUFFIX" to value as? Value
|
||||
}.filterValues { it != null } as Map<String, Value>
|
||||
|
||||
return variables.kotlinVariablesAsText(project) to variables
|
||||
}
|
||||
|
||||
private fun Map<String, Value>.kotlinVariablesAsText(project: Project): String {
|
||||
val sb = StringBuilder()
|
||||
|
||||
val psiNameHelper = PsiNameHelper.getInstance(project)
|
||||
for ((variableName, variableValue) in entries) {
|
||||
if (!psiNameHelper.isIdentifier(variableName)) continue
|
||||
|
||||
val variableTypeName = variableValue.type()?.name() ?: continue
|
||||
|
||||
val kotlinProperty = createKotlinProperty(project, variableName, variableTypeName, variableValue) ?: continue
|
||||
|
||||
sb.append("${kotlinProperty.text}\n")
|
||||
}
|
||||
|
||||
sb.append("val _debug_context_val = 1\n")
|
||||
|
||||
return sb.toString()
|
||||
}
|
||||
|
||||
private fun createKotlinProperty(project: Project, variableName: String, variableTypeName: String, value: Value): KtProperty? {
|
||||
val actualClassDescriptor = value.asValue().asmType.getClassDescriptor(GlobalSearchScope.allScope(project))
|
||||
if (actualClassDescriptor != null && actualClassDescriptor.defaultType.arguments.isEmpty()) {
|
||||
val renderedType = IdeDescriptorRenderers.SOURCE_CODE.renderType(actualClassDescriptor.defaultType.makeNullable())
|
||||
return KtPsiFactory(project).createProperty(variableName.quoteIfNeeded(), renderedType, false)
|
||||
}
|
||||
|
||||
fun String.addArraySuffix() = if (value is ArrayReference) this + "[]" else this
|
||||
|
||||
val className = variableTypeName.replace("$", ".").substringBefore("[]")
|
||||
val classType = PsiType.getTypeByName(className, project, GlobalSearchScope.allScope(project))
|
||||
val type = (if (value !is PrimitiveValue && classType.resolve() == null)
|
||||
CommonClassNames.JAVA_LANG_OBJECT
|
||||
else
|
||||
className).addArraySuffix()
|
||||
|
||||
val field = PsiElementFactory.SERVICE.getInstance(project)
|
||||
.createField(variableName, PsiType.getTypeByName(type, project, GlobalSearchScope.allScope(project)))
|
||||
val ktField = field.j2k() as? KtProperty
|
||||
ktField?.modifierList?.delete()
|
||||
return ktField
|
||||
}
|
||||
}
|
||||
|
||||
private fun wrapContextIfNeeded(project: Project, originalContext: PsiElement?, newContext: KtElement?): KtElement? {
|
||||
val markupMap: Map<*, ValueMarkup>? =
|
||||
if (ApplicationManager.getApplication().isUnitTestMode)
|
||||
NodeDescriptorImpl.getMarkupMap(originalContext?.getCopyableUserData(DEBUG_CONTEXT_FOR_TESTS)?.debugProcess)
|
||||
else
|
||||
(XDebuggerManager.getInstance(project).currentSession as? XDebugSessionImpl)?.valueMarkers?.allMarkers
|
||||
|
||||
if (markupMap == null || markupMap.isEmpty()) return newContext
|
||||
|
||||
val (text, labels) = createCodeFragmentForLabeledObjects(project, markupMap)
|
||||
if (text.isEmpty()) return newContext
|
||||
|
||||
return createWrappingContext(text, labels, newContext, project)
|
||||
}
|
||||
|
||||
private fun createFakeFileWithJavaContextElement(funWithLocalVariables: String, javaContext: PsiElement): KtFile {
|
||||
val javaFile = javaContext.containingFile as? PsiJavaFile
|
||||
|
||||
val sb = StringBuilder()
|
||||
|
||||
javaFile?.packageName?.takeUnless { it.isBlank() }?.let {
|
||||
sb.append("package ").append(it.quoteIfNeeded()).append("\n")
|
||||
}
|
||||
|
||||
javaFile?.importList?.let { sb.append(it.text).append("\n") }
|
||||
|
||||
sb.append(funWithLocalVariables)
|
||||
|
||||
return KtPsiFactory(javaContext.project).createAnalyzableFile("fakeFileForJavaContextInDebugger.kt", sb.toString(), javaContext)
|
||||
}
|
||||
|
||||
// internal for test
|
||||
private fun createWrappingContext(
|
||||
newFragmentText: String,
|
||||
labels: Map<String, Value>,
|
||||
originalContext: KtElement?,
|
||||
project: Project
|
||||
): KtElement? {
|
||||
val codeFragment = KtPsiFactory(project).createBlockCodeFragment(newFragmentText, originalContext)
|
||||
|
||||
codeFragment.accept(object : KtTreeVisitorVoid() {
|
||||
override fun visitProperty(property: KtProperty) {
|
||||
val reference = labels[property.name]
|
||||
if (reference != null) {
|
||||
property.putUserData(LABEL_VARIABLE_VALUE_KEY, reference)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
return codeFragment.getContentElement().statements.lastOrNull()
|
||||
}
|
||||
}
|
||||
@@ -133,9 +133,13 @@ class KotlinLanguageInjector(
|
||||
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)
|
||||
InjectorUtils.registerSupport(support, false, ktHost, language)
|
||||
InjectorUtils.putInjectedFileUserData(
|
||||
ktHost,
|
||||
language,
|
||||
InjectedLanguageUtil.FRANKENSTEIN_INJECTION,
|
||||
if (parts.isUnparsable) java.lang.Boolean.TRUE else null
|
||||
)
|
||||
}
|
||||
else {
|
||||
InjectorUtils.registerInjectionSimple(ktHost, baseInjection, support, registrar)
|
||||
|
||||
@@ -0,0 +1,413 @@
|
||||
/*
|
||||
* 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.openapi.util.registry.Registry
|
||||
import com.intellij.openapi.util.text.StringUtilRt
|
||||
import com.intellij.patterns.PatternCondition
|
||||
import com.intellij.patterns.PatternConditionPlus
|
||||
import com.intellij.patterns.PsiClassNamePatternCondition
|
||||
import com.intellij.patterns.ValuePatternCondition
|
||||
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.CachedValueProvider
|
||||
import com.intellij.psi.util.CachedValuesManager
|
||||
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.config.InjectionPlace
|
||||
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.idea.caches.resolve.analyze
|
||||
import org.jetbrains.kotlin.idea.patterns.KotlinFunctionPattern
|
||||
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.util.ProjectRootsUtil
|
||||
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.util.aliasImportMap
|
||||
import org.jetbrains.kotlin.utils.SmartList
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
||||
|
||||
class KotlinLanguageInjector(
|
||||
private val configuration: Configuration,
|
||||
private val project: Project,
|
||||
private val temporaryPlacesRegistry: TemporaryPlacesRegistry
|
||||
) : MultiHostInjector {
|
||||
companion object {
|
||||
private val STRING_LITERALS_REGEXP = "\"([^\"]*)\"".toRegex()
|
||||
private val ABSENT_KOTLIN_INJECTION = BaseInjection("ABSENT_KOTLIN_BASE_INJECTION")
|
||||
}
|
||||
|
||||
var annotationInjectionsEnabled = Registry.`is`("kotlin.annotation.injection.enabled", false)
|
||||
|
||||
private 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.containingFile.originalFile)) 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().progressIndicator == 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)
|
||||
?: injectInAnnotationCall(place)
|
||||
?: injectWithReceiver(place)
|
||||
?: injectWithVariableUsage(place, originalHost)
|
||||
}
|
||||
|
||||
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()) return null
|
||||
|
||||
val kotlinInjections = configuration.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 ktProperty = host.parent as? KtProperty ?: return null
|
||||
if (ktProperty.initializer != host) return null
|
||||
|
||||
if (isAnalyzeOff()) 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, KtCallElement::class.java) ?: return null
|
||||
val callee = getNameReference(callExpression.calleeExpression) ?: return null
|
||||
|
||||
if (isAnalyzeOff()) 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 getNameReference(callee: KtExpression?): KtNameReferenceExpression? {
|
||||
if (callee is KtConstructorCalleeExpression)
|
||||
return callee.constructorReferenceExpression as? KtNameReferenceExpression
|
||||
return callee as? KtNameReferenceExpression
|
||||
}
|
||||
|
||||
private fun injectInAnnotationCall(host: KtElement): InjectionInfo? {
|
||||
if (!annotationInjectionsEnabled) return null
|
||||
val argument = host.parent as? KtValueArgument ?: return null
|
||||
val annotationEntry = argument.parent.parent as? KtCallElement ?: return null
|
||||
if (!fastCheckInjectionsExists(annotationEntry)) return null
|
||||
val calleeExpression = annotationEntry.calleeExpression ?: return null
|
||||
val callee = getNameReference(calleeExpression)?.mainReference?.resolve()
|
||||
when (callee) {
|
||||
is PsiClass -> {
|
||||
val psiClass = callee as? PsiClass ?: return null
|
||||
val argumentName = argument.getArgumentName()?.asName?.identifier ?: "value"
|
||||
val method = psiClass.findMethodsByName(argumentName, false).singleOrNull() ?: return null
|
||||
return findInjection(method, configuration.getInjections(JavaLanguageInjectionSupport.JAVA_SUPPORT_ID))
|
||||
}
|
||||
else -> 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.getInjections(JavaLanguageInjectionSupport.JAVA_SUPPORT_ID))
|
||||
if (injectionInfo != null) {
|
||||
return injectionInfo
|
||||
}
|
||||
|
||||
val annotations = AnnotationUtilEx.getAnnotationFrom(
|
||||
psiParameter,
|
||||
configuration.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.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
|
||||
val injectAnnotation = parameterDescriptor.annotations.findAnnotation(FqName(AnnotationUtil.LANGUAGE)) ?: return null
|
||||
|
||||
val languageId = injectAnnotation.argumentValue("value")?.safeAs<StringValue>()?.value ?: return null
|
||||
return InjectionInfo(languageId, null, 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(): Boolean {
|
||||
return configuration.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)
|
||||
}
|
||||
|
||||
private val injectableTargetClassShortNames = CachedValuesManager.getManager(project)
|
||||
.createCachedValue({
|
||||
CachedValueProvider.Result.create(HashSet<String>().apply {
|
||||
for (injection in configuration.getInjections(JavaLanguageInjectionSupport.JAVA_SUPPORT_ID)) {
|
||||
for (injectionPlace in injection.injectionPlaces) {
|
||||
for (targetClassFQN in retrieveJavaPlaceTargetClassesFQNs(injectionPlace)) {
|
||||
add(StringUtilRt.getShortName(targetClassFQN))
|
||||
}
|
||||
}
|
||||
}
|
||||
for (injection in configuration.getInjections(org.jetbrains.kotlin.idea.injection.KOTLIN_SUPPORT_ID)) {
|
||||
for (injectionPlace in injection.injectionPlaces) {
|
||||
for (targetClassFQN in retrieveKotlinPlaceTargetClassesFQNs(injectionPlace)) {
|
||||
add(StringUtilRt.getShortName(targetClassFQN))
|
||||
}
|
||||
}
|
||||
}
|
||||
}, configuration)
|
||||
}, false)
|
||||
|
||||
private fun fastCheckInjectionsExists(annotationEntry: KtCallElement): Boolean {
|
||||
val referencedName = getNameReference(annotationEntry.calleeExpression)?.getReferencedName() ?: return false
|
||||
val annotationShortName = annotationEntry.containingKtFile.aliasImportMap()[referencedName].singleOrNull() ?: referencedName
|
||||
return annotationShortName in injectableTargetClassShortNames.value
|
||||
}
|
||||
|
||||
private fun retrieveJavaPlaceTargetClassesFQNs(place: InjectionPlace): Collection<String> {
|
||||
val classCondition = place.elementPattern.condition.conditions.firstOrNull { it.debugMethodName == "definedInClass" }
|
||||
as? PatternConditionPlus<*, *> ?: return emptyList()
|
||||
val psiClassNamePatternCondition = classCondition.valuePattern.condition.conditions.
|
||||
firstIsInstanceOrNull<PsiClassNamePatternCondition>() ?: return emptyList()
|
||||
val valuePatternCondition = psiClassNamePatternCondition.namePattern.condition.conditions.firstIsInstanceOrNull<ValuePatternCondition<String>>() ?: return emptyList()
|
||||
return valuePatternCondition.values
|
||||
}
|
||||
|
||||
private fun retrieveKotlinPlaceTargetClassesFQNs(place: InjectionPlace): Collection<String> {
|
||||
val classNames = SmartList<String>()
|
||||
fun collect(condition: PatternCondition<*>) {
|
||||
when (condition) {
|
||||
is PatternConditionPlus<*, *> -> condition.valuePattern.condition.conditions.forEach { collect(it) }
|
||||
is KotlinFunctionPattern.DefinedInClassCondition -> classNames.add(condition.fqName)
|
||||
}
|
||||
}
|
||||
place.elementPattern.condition.conditions.forEach { collect(it) }
|
||||
return classNames
|
||||
}
|
||||
|
||||
}
|
||||
@@ -47,6 +47,7 @@ dependencies {
|
||||
testRuntime(intellijPluginDep("coverage"))
|
||||
testRuntime(intellijPluginDep("maven"))
|
||||
testRuntime(intellijPluginDep("android"))
|
||||
testRuntime(intellijPluginDep("smali"))
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
|
||||
plugins {
|
||||
kotlin("jvm")
|
||||
id("jps-compatible")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compile(project(":core:util.runtime"))
|
||||
compile(project(":compiler:frontend"))
|
||||
compile(project(":compiler:frontend.java"))
|
||||
compile(project(":compiler:util"))
|
||||
compile(project(":compiler:cli-common"))
|
||||
compile(project(":kotlin-build-common"))
|
||||
|
||||
compile(project(":js:js.frontend"))
|
||||
|
||||
compile(project(":idea"))
|
||||
compile(project(":idea:idea-jvm"))
|
||||
compile(project(":idea:idea-jps-common"))
|
||||
|
||||
compileOnly(intellijDep())
|
||||
excludeInAndroidStudio(rootProject) { compileOnly(intellijPluginDep("maven")) }
|
||||
|
||||
testCompile(projectTests(":idea"))
|
||||
testCompile(projectTests(":compiler:tests-common"))
|
||||
testCompile(projectTests(":idea:idea-test-framework"))
|
||||
|
||||
testCompileOnly(intellijDep())
|
||||
testCompileOnly(intellijPluginDep("maven"))
|
||||
|
||||
testRuntime(projectDist(":kotlin-reflect"))
|
||||
testRuntime(project(":idea:idea-jvm"))
|
||||
testRuntime(project(":idea:idea-android"))
|
||||
testRuntime(project(":plugins:android-extensions-ide"))
|
||||
testRuntime(project(":plugins:lint"))
|
||||
testRuntime(project(":sam-with-receiver-ide-plugin"))
|
||||
testRuntime(project(":allopen-ide-plugin"))
|
||||
testRuntime(project(":noarg-ide-plugin"))
|
||||
|
||||
testRuntime(intellijDep())
|
||||
// TODO: the order of the plugins matters here, consider avoiding order-dependency
|
||||
testRuntime(intellijPluginDep("junit"))
|
||||
testRuntime(intellijPluginDep("testng"))
|
||||
testRuntime(intellijPluginDep("properties"))
|
||||
testRuntime(intellijPluginDep("gradle"))
|
||||
testRuntime(intellijPluginDep("Groovy"))
|
||||
testRuntime(intellijPluginDep("coverage"))
|
||||
testRuntime(intellijPluginDep("maven"))
|
||||
testRuntime(intellijPluginDep("android"))
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
"main" { projectDefault() }
|
||||
"test" { projectDefault() }
|
||||
}
|
||||
|
||||
testsJar()
|
||||
|
||||
projectTest {
|
||||
workingDir = rootDir
|
||||
}
|
||||
@@ -373,18 +373,16 @@ private enum class SourceType {
|
||||
|
||||
@State(
|
||||
name = "AutoImportedSourceRoots",
|
||||
storages = [(Storage(id = "other", file = StoragePathMacros.MODULE_FILE))]
|
||||
storages = [(Storage(file = StoragePathMacros.MODULE_FILE))]
|
||||
)
|
||||
class KotlinImporterComponent : PersistentStateComponent<KotlinImporterComponent.State> {
|
||||
class State(var directories: List<String> = ArrayList())
|
||||
|
||||
val addedSources: MutableSet<String> = Collections.synchronizedSet(HashSet<String>())
|
||||
|
||||
override fun loadState(state: State?) {
|
||||
override fun loadState(state: State) {
|
||||
addedSources.clear()
|
||||
if (state != null) {
|
||||
addedSources.addAll(state.directories)
|
||||
}
|
||||
addedSources.addAll(state.directories)
|
||||
}
|
||||
|
||||
override fun getState(): State {
|
||||
|
||||
@@ -0,0 +1,396 @@
|
||||
/*
|
||||
* 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.maven
|
||||
|
||||
import com.intellij.openapi.components.PersistentStateComponent
|
||||
import com.intellij.openapi.components.State
|
||||
import com.intellij.openapi.components.Storage
|
||||
import com.intellij.openapi.components.StoragePathMacros
|
||||
import com.intellij.openapi.externalSystem.service.project.IdeModifiableModelsProvider
|
||||
import com.intellij.openapi.module.Module
|
||||
import com.intellij.openapi.roots.CompilerModuleExtension
|
||||
import com.intellij.openapi.roots.ModifiableRootModel
|
||||
import com.intellij.openapi.roots.OrderEnumerator
|
||||
import com.intellij.openapi.roots.OrderRootType
|
||||
import com.intellij.openapi.roots.impl.libraries.LibraryEx
|
||||
import com.intellij.openapi.roots.libraries.Library
|
||||
import com.intellij.openapi.util.AsyncResult
|
||||
import com.intellij.util.PathUtil
|
||||
import org.jdom.Element
|
||||
import org.jdom.Text
|
||||
import org.jetbrains.idea.maven.importing.MavenImporter
|
||||
import org.jetbrains.idea.maven.importing.MavenRootModelAdapter
|
||||
import org.jetbrains.idea.maven.model.MavenPlugin
|
||||
import org.jetbrains.idea.maven.project.*
|
||||
import org.jetbrains.jps.model.java.JavaSourceRootType
|
||||
import org.jetbrains.jps.model.module.JpsModuleSourceRootType
|
||||
import org.jetbrains.kotlin.cli.common.arguments.K2JSCompilerArguments
|
||||
import org.jetbrains.kotlin.cli.common.arguments.K2JVMCompilerArguments
|
||||
import org.jetbrains.kotlin.cli.common.arguments.K2MetadataCompilerArguments
|
||||
import org.jetbrains.kotlin.cli.common.arguments.parseCommandLineArguments
|
||||
import org.jetbrains.kotlin.compilerRunner.ArgumentUtils
|
||||
import org.jetbrains.kotlin.config.*
|
||||
import org.jetbrains.kotlin.extensions.ProjectExtensionDescriptor
|
||||
import org.jetbrains.kotlin.idea.facet.*
|
||||
import org.jetbrains.kotlin.idea.framework.detectLibraryKind
|
||||
import org.jetbrains.kotlin.idea.framework.libraryKind
|
||||
import org.jetbrains.kotlin.idea.maven.configuration.KotlinMavenConfigurator
|
||||
import org.jetbrains.kotlin.utils.SmartList
|
||||
import org.jetbrains.kotlin.utils.addIfNotNull
|
||||
import java.io.File
|
||||
import java.util.*
|
||||
|
||||
interface MavenProjectImportHandler {
|
||||
companion object : ProjectExtensionDescriptor<MavenProjectImportHandler>(
|
||||
"org.jetbrains.kotlin.mavenProjectImportHandler",
|
||||
MavenProjectImportHandler::class.java
|
||||
)
|
||||
|
||||
operator fun invoke(facet: KotlinFacet, mavenProject: MavenProject)
|
||||
}
|
||||
|
||||
class KotlinMavenImporter : MavenImporter(KOTLIN_PLUGIN_GROUP_ID, KOTLIN_PLUGIN_ARTIFACT_ID) {
|
||||
companion object {
|
||||
const val KOTLIN_PLUGIN_GROUP_ID = "org.jetbrains.kotlin"
|
||||
const val KOTLIN_PLUGIN_ARTIFACT_ID = "kotlin-maven-plugin"
|
||||
|
||||
const val KOTLIN_PLUGIN_SOURCE_DIRS_CONFIG = "sourceDirs"
|
||||
}
|
||||
|
||||
override fun preProcess(
|
||||
module: Module,
|
||||
mavenProject: MavenProject,
|
||||
changes: MavenProjectChanges,
|
||||
modifiableModelsProvider: IdeModifiableModelsProvider
|
||||
) {
|
||||
}
|
||||
|
||||
override fun process(
|
||||
modifiableModelsProvider: IdeModifiableModelsProvider,
|
||||
module: Module,
|
||||
rootModel: MavenRootModelAdapter,
|
||||
mavenModel: MavenProjectsTree,
|
||||
mavenProject: MavenProject,
|
||||
changes: MavenProjectChanges,
|
||||
mavenProjectToModuleName: MutableMap<MavenProject, String>,
|
||||
postTasks: MutableList<MavenProjectsProcessorTask>
|
||||
) {
|
||||
|
||||
if (changes.plugins) {
|
||||
contributeSourceDirectories(mavenProject, module, rootModel)
|
||||
}
|
||||
}
|
||||
|
||||
override fun postProcess(
|
||||
module: Module,
|
||||
mavenProject: MavenProject,
|
||||
changes: MavenProjectChanges,
|
||||
modifiableModelsProvider: IdeModifiableModelsProvider
|
||||
) {
|
||||
super.postProcess(module, mavenProject, changes, modifiableModelsProvider)
|
||||
|
||||
if (changes.dependencies) {
|
||||
scheduleDownloadStdlibSources(mavenProject, module)
|
||||
|
||||
val targetLibraryKind = detectPlatformByExecutions(mavenProject)?.libraryKind
|
||||
if (targetLibraryKind != null) {
|
||||
modifiableModelsProvider.getModifiableRootModel(module).orderEntries().forEachLibrary { library ->
|
||||
if ((library as LibraryEx).kind == null) {
|
||||
val model = modifiableModelsProvider.getModifiableLibraryModel(library) as LibraryEx.ModifiableModelEx
|
||||
detectLibraryKind(model.getFiles(OrderRootType.CLASSES))?.let { model.kind = it }
|
||||
}
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
configureFacet(mavenProject, modifiableModelsProvider, module)
|
||||
}
|
||||
|
||||
private fun scheduleDownloadStdlibSources(mavenProject: MavenProject, module: Module) {
|
||||
// TODO: here we have to process all kotlin libraries but for now we only handle standard libraries
|
||||
val artifacts = mavenProject.dependencyArtifactIndex.data[KOTLIN_PLUGIN_GROUP_ID]?.values?.flatMap { it.filter { it.isResolved } }
|
||||
?: emptyList()
|
||||
|
||||
val librariesWithNoSources = ArrayList<Library>()
|
||||
OrderEnumerator.orderEntries(module).forEachLibrary { library ->
|
||||
if (library.modifiableModel.getFiles(OrderRootType.SOURCES).isEmpty()) {
|
||||
librariesWithNoSources.add(library)
|
||||
}
|
||||
true
|
||||
}
|
||||
val libraryNames = librariesWithNoSources.mapTo(HashSet()) { it.name }
|
||||
val toBeDownloaded = artifacts.filter { it.libraryName in libraryNames }
|
||||
|
||||
if (toBeDownloaded.isNotEmpty()) {
|
||||
MavenProjectsManager.getInstance(module.project)
|
||||
.scheduleArtifactsDownloading(listOf(mavenProject), toBeDownloaded, true, false, AsyncResult())
|
||||
}
|
||||
}
|
||||
|
||||
private fun configureJSOutputPaths(
|
||||
mavenProject: MavenProject,
|
||||
modifiableRootModel: ModifiableRootModel,
|
||||
facetSettings: KotlinFacetSettings,
|
||||
mavenPlugin: MavenPlugin
|
||||
) {
|
||||
fun parentPath(path: String): String =
|
||||
File(path).absoluteFile.parentFile.absolutePath
|
||||
|
||||
val sharedOutputFile = mavenPlugin.configurationElement?.getChild("outputFile")?.text
|
||||
|
||||
val compilerModuleExtension = modifiableRootModel.getModuleExtension(CompilerModuleExtension::class.java) ?: return
|
||||
val buildDirectory = mavenProject.buildDirectory
|
||||
|
||||
val executions = mavenPlugin.executions
|
||||
|
||||
executions.forEach {
|
||||
val explicitOutputFile = it.configurationElement?.getChild("outputFile")?.text ?: sharedOutputFile
|
||||
if (PomFile.KotlinGoals.Js in it.goals) {
|
||||
// see org.jetbrains.kotlin.maven.K2JSCompilerMojo
|
||||
val outputFilePath = PathUtil.toSystemDependentName(explicitOutputFile ?: "$buildDirectory/js/${mavenProject.mavenId.artifactId}.js")
|
||||
compilerModuleExtension.setCompilerOutputPath(parentPath(outputFilePath))
|
||||
facetSettings.productionOutputPath = outputFilePath
|
||||
}
|
||||
if (PomFile.KotlinGoals.TestJs in it.goals) {
|
||||
// see org.jetbrains.kotlin.maven.KotlinTestJSCompilerMojo
|
||||
val outputFilePath = PathUtil.toSystemDependentName(explicitOutputFile ?: "$buildDirectory/test-js/${mavenProject.mavenId.artifactId}-tests.js")
|
||||
compilerModuleExtension.setCompilerOutputPathForTests(parentPath(outputFilePath))
|
||||
facetSettings.testOutputPath = outputFilePath
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getCompilerArgumentsByConfigurationElement(
|
||||
mavenProject: MavenProject,
|
||||
configuration: Element?,
|
||||
platform: TargetPlatformKind<*>
|
||||
): List<String> {
|
||||
val arguments = when (platform) {
|
||||
is TargetPlatformKind.Jvm -> K2JVMCompilerArguments()
|
||||
TargetPlatformKind.JavaScript -> K2JSCompilerArguments()
|
||||
TargetPlatformKind.Common -> K2MetadataCompilerArguments()
|
||||
}
|
||||
|
||||
arguments.apiVersion = configuration?.getChild("apiVersion")?.text ?:
|
||||
mavenProject.properties["kotlin.compiler.apiVersion"]?.toString()
|
||||
arguments.languageVersion = configuration?.getChild("languageVersion")?.text ?:
|
||||
mavenProject.properties["kotlin.compiler.languageVersion"]?.toString()
|
||||
arguments.multiPlatform = configuration?.getChild("multiPlatform")?.text?.trim()?.toBoolean() ?: false
|
||||
arguments.suppressWarnings = configuration?.getChild("nowarn")?.text?.trim()?.toBoolean() ?: false
|
||||
|
||||
configuration?.getChild("experimentalCoroutines")?.text?.trim()?.let {
|
||||
arguments.coroutinesState = it
|
||||
}
|
||||
|
||||
when (arguments) {
|
||||
is K2JVMCompilerArguments -> {
|
||||
arguments.classpath = configuration?.getChild("classpath")?.text
|
||||
arguments.jdkHome = configuration?.getChild("jdkHome")?.text
|
||||
arguments.jvmTarget = configuration?.getChild("jvmTarget")?.text ?:
|
||||
mavenProject.properties["kotlin.compiler.jvmTarget"]?.toString()
|
||||
arguments.javaParameters = configuration?.getChild("javaParameters")?.text?.toBoolean() ?: false
|
||||
}
|
||||
is K2JSCompilerArguments -> {
|
||||
arguments.sourceMap = configuration?.getChild("sourceMap")?.text?.trim()?.toBoolean() ?: false
|
||||
arguments.sourceMapPrefix = configuration?.getChild("sourceMapPrefix")?.text?.trim() ?: ""
|
||||
arguments.sourceMapEmbedSources = configuration?.getChild("sourceMapEmbedSources")?.text?.trim() ?: "inlining"
|
||||
arguments.outputFile = configuration?.getChild("outputFile")?.text
|
||||
arguments.metaInfo = configuration?.getChild("metaInfo")?.text?.trim()?.toBoolean() ?: false
|
||||
arguments.moduleKind = configuration?.getChild("moduleKind")?.text
|
||||
arguments.main = configuration?.getChild("main")?.text
|
||||
}
|
||||
}
|
||||
|
||||
val additionalArgs = SmartList<String>().apply {
|
||||
val argsElement = configuration?.getChild("args")
|
||||
|
||||
argsElement?.content?.forEach { argElement ->
|
||||
when (argElement) {
|
||||
is Text -> {
|
||||
argElement.text?.let { addAll(splitArgumentString(it)) }
|
||||
}
|
||||
is Element -> {
|
||||
if (argElement.name == "arg") {
|
||||
addIfNotNull(argElement.text)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
parseCommandLineArguments(additionalArgs, arguments)
|
||||
|
||||
return ArgumentUtils.convertArgumentsToStringList(arguments)
|
||||
}
|
||||
|
||||
private val compilationGoals = listOf(
|
||||
PomFile.KotlinGoals.Compile,
|
||||
PomFile.KotlinGoals.TestCompile,
|
||||
PomFile.KotlinGoals.Js,
|
||||
PomFile.KotlinGoals.TestJs,
|
||||
PomFile.KotlinGoals.MetaData
|
||||
)
|
||||
|
||||
private fun configureFacet(mavenProject: MavenProject, modifiableModelsProvider: IdeModifiableModelsProvider, module: Module) {
|
||||
val mavenPlugin = mavenProject.findPlugin(KotlinMavenConfigurator.GROUP_ID, KotlinMavenConfigurator.MAVEN_PLUGIN_ID) ?: return
|
||||
val compilerVersion = mavenPlugin.version ?: LanguageVersion.LATEST_STABLE.versionString
|
||||
val kotlinFacet = module.getOrCreateFacet(modifiableModelsProvider, false)
|
||||
val platform = detectPlatform(mavenProject)
|
||||
|
||||
kotlinFacet.configureFacet(compilerVersion, LanguageFeature.Coroutines.defaultState, platform, modifiableModelsProvider)
|
||||
val facetSettings = kotlinFacet.configuration.settings
|
||||
val configuredPlatform = kotlinFacet.configuration.settings.targetPlatformKind!!
|
||||
val configuration = mavenPlugin.configurationElement
|
||||
val sharedArguments = getCompilerArgumentsByConfigurationElement(mavenProject, configuration, configuredPlatform)
|
||||
val executionArguments = mavenPlugin.executions
|
||||
?.firstOrNull { it.goals.any { it in compilationGoals } }
|
||||
?.configurationElement?.let { getCompilerArgumentsByConfigurationElement(mavenProject, it, configuredPlatform) }
|
||||
parseCompilerArgumentsToFacet(sharedArguments, emptyList(), kotlinFacet, modifiableModelsProvider)
|
||||
if (executionArguments != null) {
|
||||
parseCompilerArgumentsToFacet(executionArguments, emptyList(), kotlinFacet, modifiableModelsProvider)
|
||||
}
|
||||
if (facetSettings.compilerArguments is K2JSCompilerArguments) {
|
||||
configureJSOutputPaths(mavenProject, modifiableModelsProvider.getModifiableRootModel(module), facetSettings, mavenPlugin)
|
||||
}
|
||||
MavenProjectImportHandler.getInstances(module.project).forEach { it(kotlinFacet, mavenProject) }
|
||||
setImplementedModuleName(kotlinFacet, mavenProject, module)
|
||||
kotlinFacet.noVersionAutoAdvance()
|
||||
}
|
||||
|
||||
private fun detectPlatform(mavenProject: MavenProject) =
|
||||
detectPlatformByExecutions(mavenProject) ?: detectPlatformByLibraries(mavenProject)
|
||||
|
||||
private fun detectPlatformByExecutions(mavenProject: MavenProject): TargetPlatformKind<*>? {
|
||||
return mavenProject.findPlugin(KOTLIN_PLUGIN_GROUP_ID, KOTLIN_PLUGIN_ARTIFACT_ID)?.executions?.flatMap { it.goals }
|
||||
?.mapNotNull { goal ->
|
||||
when (goal) {
|
||||
PomFile.KotlinGoals.Compile, PomFile.KotlinGoals.TestCompile -> TargetPlatformKind.Jvm[JvmTarget.JVM_1_6]
|
||||
PomFile.KotlinGoals.Js, PomFile.KotlinGoals.TestJs -> TargetPlatformKind.JavaScript
|
||||
PomFile.KotlinGoals.MetaData -> TargetPlatformKind.Common
|
||||
else -> null
|
||||
}
|
||||
}?.distinct()?.singleOrNull()
|
||||
}
|
||||
|
||||
private fun detectPlatformByLibraries(mavenProject: MavenProject): TargetPlatformKind<*>? {
|
||||
return TargetPlatformKind.ALL_PLATFORMS.firstOrNull {
|
||||
it.mavenLibraryIds.any { mavenProject.findDependencies(KOTLIN_PLUGIN_GROUP_ID, it).isNotEmpty() }
|
||||
}
|
||||
}
|
||||
|
||||
// TODO in theory it should work like this but it doesn't as it couldn't unmark source roots that are not roots anymore.
|
||||
// I believe this is something should be done by the underlying maven importer implementation or somewhere else in the IDEA
|
||||
// For now there is a contributeSourceDirectories implementation that deals with the issue
|
||||
// see https://youtrack.jetbrains.com/issue/IDEA-148280
|
||||
|
||||
// override fun collectSourceRoots(mavenProject: MavenProject, result: PairConsumer<String, JpsModuleSourceRootType<*>>) {
|
||||
// for ((type, dir) in collectSourceDirectories(mavenProject)) {
|
||||
// val jpsType: JpsModuleSourceRootType<*> = when (type) {
|
||||
// SourceType.PROD -> JavaSourceRootType.SOURCE
|
||||
// SourceType.TEST -> JavaSourceRootType.TEST_SOURCE
|
||||
// }
|
||||
//
|
||||
// result.consume(dir, jpsType)
|
||||
// }
|
||||
// }
|
||||
|
||||
private fun contributeSourceDirectories(mavenProject: MavenProject, module: Module, rootModel: MavenRootModelAdapter) {
|
||||
val directories = collectSourceDirectories(mavenProject)
|
||||
|
||||
val toBeAdded = directories.map { it.second }.toSet()
|
||||
val state = module.kotlinImporterComponent
|
||||
|
||||
for ((type, dir) in directories) {
|
||||
if (rootModel.getSourceFolder(File(dir)) == null) {
|
||||
val jpsType: JpsModuleSourceRootType<*> = when (type) {
|
||||
SourceType.TEST -> JavaSourceRootType.TEST_SOURCE
|
||||
SourceType.PROD -> JavaSourceRootType.SOURCE
|
||||
}
|
||||
|
||||
rootModel.addSourceFolder(dir, jpsType)
|
||||
}
|
||||
}
|
||||
|
||||
state.addedSources.filter { it !in toBeAdded }.forEach {
|
||||
rootModel.unregisterAll(it, true, true)
|
||||
state.addedSources.remove(it)
|
||||
}
|
||||
state.addedSources.addAll(toBeAdded)
|
||||
}
|
||||
|
||||
private fun collectSourceDirectories(mavenProject: MavenProject): List<Pair<SourceType, String>> =
|
||||
mavenProject.plugins.filter { it.isKotlinPlugin() }.flatMap { plugin ->
|
||||
plugin.configurationElement.sourceDirectories().map { SourceType.PROD to it } +
|
||||
plugin.executions.flatMap { execution ->
|
||||
execution.configurationElement.sourceDirectories().map { execution.sourceType() to it }
|
||||
}
|
||||
}.distinct()
|
||||
|
||||
private fun setImplementedModuleName(kotlinFacet: KotlinFacet, mavenProject: MavenProject, module: Module) {
|
||||
if (kotlinFacet.configuration.settings.targetPlatformKind == TargetPlatformKind.Common) {
|
||||
kotlinFacet.configuration.settings.implementedModuleNames = emptyList()
|
||||
} else {
|
||||
val manager = MavenProjectsManager.getInstance(module.project)
|
||||
val mavenDependencies = mavenProject.dependencies.mapNotNull { manager?.findProject(it) }
|
||||
val implemented = mavenDependencies.filter { detectPlatformByExecutions(it) == TargetPlatformKind.Common }
|
||||
|
||||
kotlinFacet.configuration.settings.implementedModuleNames = implemented.map { manager.findModule(it)?.name ?: it.displayName }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun MavenPlugin.isKotlinPlugin() =
|
||||
groupId == KotlinMavenImporter.KOTLIN_PLUGIN_GROUP_ID && artifactId == KotlinMavenImporter.KOTLIN_PLUGIN_ARTIFACT_ID
|
||||
|
||||
private fun Element?.sourceDirectories(): List<String> =
|
||||
this?.getChildren(KotlinMavenImporter.KOTLIN_PLUGIN_SOURCE_DIRS_CONFIG)?.flatMap { it.children ?: emptyList() }?.map { it.textTrim }
|
||||
?: emptyList()
|
||||
|
||||
private fun MavenPlugin.Execution.sourceType() =
|
||||
goals.map { if (isTestGoalName(it)) SourceType.TEST else SourceType.PROD }
|
||||
.distinct()
|
||||
.singleOrNull() ?: SourceType.PROD
|
||||
|
||||
private fun isTestGoalName(goalName: String) = goalName.startsWith("test-")
|
||||
|
||||
private enum class SourceType {
|
||||
PROD, TEST
|
||||
}
|
||||
|
||||
@State(
|
||||
name = "AutoImportedSourceRoots",
|
||||
storages = [(Storage(id = "other", file = StoragePathMacros.MODULE_FILE))]
|
||||
)
|
||||
class KotlinImporterComponent : PersistentStateComponent<KotlinImporterComponent.State> {
|
||||
class State(var directories: List<String> = ArrayList())
|
||||
|
||||
val addedSources: MutableSet<String> = Collections.synchronizedSet(HashSet<String>())
|
||||
|
||||
override fun loadState(state: State?) {
|
||||
addedSources.clear()
|
||||
if (state != null) {
|
||||
addedSources.addAll(state.directories)
|
||||
}
|
||||
}
|
||||
|
||||
override fun getState(): State {
|
||||
return State(addedSources.sorted())
|
||||
}
|
||||
}
|
||||
|
||||
private val Module.kotlinImporterComponent: KotlinImporterComponent
|
||||
get() = getComponent(KotlinImporterComponent::class.java) ?: throw IllegalStateException("No maven importer state configured")
|
||||
@@ -26,6 +26,7 @@
|
||||
<groupId>org.jetbrains.kotlin</groupId>
|
||||
<artifactId>kotlin-reflect</artifactId>
|
||||
<version>RELEASE</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>maventest</groupId>
|
||||
<artifactId>maventest</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
<properties>
|
||||
<kotlin.version>$VERSION$</kotlin.version>
|
||||
</properties>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.jetbrains.kotlin</groupId>
|
||||
<artifactId>kotlin-stdlib-jre8</artifactId>
|
||||
<version>${kotlin.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jetbrains.kotlin</groupId>
|
||||
<artifactId>kotlin-test</artifactId>
|
||||
<version>${kotlin.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jetbrains.kotlin</groupId>
|
||||
<artifactId>kotlin-reflect</artifactId>
|
||||
<version>RELEASE</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<sourceDirectory>${project.basedir}/src/main/kotlin</sourceDirectory>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.jetbrains.kotlin</groupId>
|
||||
<artifactId>kotlin-maven-plugin</artifactId>
|
||||
<version>${kotlin.version}</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>compile</id>
|
||||
<phase>compile</phase>
|
||||
<goals>
|
||||
<goal>wrong-goal</goal>
|
||||
<goal>compile</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>test-compile</id>
|
||||
<phase>test-compile</phase>
|
||||
<goals>
|
||||
<goal>test-compile</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
|
||||
</project>
|
||||
-1
@@ -69,7 +69,6 @@ abstract class KotlinLightCodeInsightFixtureTestCase : KotlinLightCodeInsightFix
|
||||
}
|
||||
})
|
||||
}
|
||||
CodeInsightTestCase.fixTemplates()
|
||||
}
|
||||
|
||||
override fun tearDown() {
|
||||
|
||||
+231
@@ -0,0 +1,231 @@
|
||||
/*
|
||||
* 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.test
|
||||
|
||||
import com.intellij.codeInsight.CodeInsightTestCase
|
||||
import com.intellij.codeInsight.daemon.impl.EditorTracker
|
||||
import com.intellij.ide.startup.impl.StartupManagerImpl
|
||||
import com.intellij.openapi.actionSystem.ActionPlaces
|
||||
import com.intellij.openapi.actionSystem.AnActionEvent
|
||||
import com.intellij.openapi.actionSystem.Presentation
|
||||
import com.intellij.openapi.actionSystem.ex.ActionManagerEx
|
||||
import com.intellij.openapi.application.WriteAction
|
||||
import com.intellij.openapi.editor.ex.EditorEx
|
||||
import com.intellij.openapi.externalSystem.service.project.IdeModifiableModelsProviderImpl
|
||||
import com.intellij.openapi.module.Module
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.startup.StartupManager
|
||||
import com.intellij.openapi.util.io.FileUtil
|
||||
import com.intellij.openapi.util.text.StringUtil
|
||||
import com.intellij.openapi.vfs.VfsUtilCore
|
||||
import com.intellij.openapi.vfs.newvfs.impl.VfsRootAccess
|
||||
import com.intellij.psi.PsiManager
|
||||
import com.intellij.psi.search.FileTypeIndex
|
||||
import com.intellij.psi.search.ProjectScope
|
||||
import com.intellij.testFramework.LightProjectDescriptor
|
||||
import com.intellij.testFramework.LoggedErrorProcessor
|
||||
import org.apache.log4j.Logger
|
||||
import org.jetbrains.kotlin.cli.common.arguments.K2JVMCompilerArguments
|
||||
import org.jetbrains.kotlin.config.*
|
||||
import org.jetbrains.kotlin.idea.KotlinFileType
|
||||
import org.jetbrains.kotlin.idea.facet.KotlinFacet
|
||||
import org.jetbrains.kotlin.idea.facet.configureFacet
|
||||
import org.jetbrains.kotlin.idea.facet.getOrCreateFacet
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.test.InTextDirectivesUtils
|
||||
import org.jetbrains.kotlin.test.KotlinTestUtils
|
||||
import org.jetbrains.kotlin.test.TestMetadata
|
||||
import org.jetbrains.kotlin.utils.addIfNotNull
|
||||
import org.jetbrains.kotlin.utils.rethrow
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
import java.util.*
|
||||
import kotlin.reflect.full.findAnnotation
|
||||
|
||||
abstract class KotlinLightCodeInsightFixtureTestCase : KotlinLightCodeInsightFixtureTestCaseBase() {
|
||||
private val exceptions = ArrayList<Throwable>()
|
||||
|
||||
protected val module: Module get() = myFixture.module
|
||||
|
||||
protected open val captureExceptions = true
|
||||
|
||||
override fun setUp() {
|
||||
super.setUp()
|
||||
(StartupManager.getInstance(project) as StartupManagerImpl).runPostStartupActivities()
|
||||
VfsRootAccess.allowRootAccess(KotlinTestUtils.getHomeDirectory())
|
||||
|
||||
project.getComponent(EditorTracker::class.java)?.projectOpened()
|
||||
|
||||
invalidateLibraryCache(project)
|
||||
|
||||
if (captureExceptions) {
|
||||
LoggedErrorProcessor.setNewInstance(object : LoggedErrorProcessor() {
|
||||
override fun processError(message: String?, t: Throwable?, details: Array<out String>?, logger: Logger) {
|
||||
exceptions.addIfNotNull(t)
|
||||
super.processError(message, t, details, logger)
|
||||
}
|
||||
})
|
||||
}
|
||||
CodeInsightTestCase.fixTemplates()
|
||||
}
|
||||
|
||||
override fun tearDown() {
|
||||
LoggedErrorProcessor.restoreDefaultProcessor()
|
||||
|
||||
VfsRootAccess.disallowRootAccess(KotlinTestUtils.getHomeDirectory())
|
||||
|
||||
doKotlinTearDown(project) {
|
||||
super.tearDown()
|
||||
}
|
||||
|
||||
if (exceptions.isNotEmpty()) {
|
||||
exceptions.forEach { it.printStackTrace() }
|
||||
throw AssertionError("Exceptions in other threads happened")
|
||||
}
|
||||
}
|
||||
|
||||
override fun getProjectDescriptor(): LightProjectDescriptor
|
||||
= getProjectDescriptorFromFileDirective()
|
||||
|
||||
protected fun getProjectDescriptorFromTestName(): LightProjectDescriptor {
|
||||
val testName = StringUtil.toLowerCase(getTestName(false))
|
||||
|
||||
if (testName.endsWith("runtime")) {
|
||||
return KotlinWithJdkAndRuntimeLightProjectDescriptor.INSTANCE
|
||||
}
|
||||
else if (testName.endsWith("stdlib")) {
|
||||
return ProjectDescriptorWithStdlibSources.INSTANCE
|
||||
}
|
||||
|
||||
return KotlinLightProjectDescriptor.INSTANCE
|
||||
}
|
||||
|
||||
protected fun getProjectDescriptorFromFileDirective(): LightProjectDescriptor {
|
||||
if (!isAllFilesPresentInTest()) {
|
||||
try {
|
||||
val fileText = FileUtil.loadFile(File(testDataPath, fileName()), true)
|
||||
|
||||
val withLibraryDirective = InTextDirectivesUtils.findLinesWithPrefixesRemoved(fileText, "WITH_LIBRARY:")
|
||||
if (!withLibraryDirective.isEmpty()) {
|
||||
return SdkAndMockLibraryProjectDescriptor(
|
||||
PluginTestCaseBase.getTestDataPathBase() + "/" + withLibraryDirective.get(
|
||||
0
|
||||
), true
|
||||
)
|
||||
}
|
||||
else if (InTextDirectivesUtils.isDirectiveDefined(fileText, "RUNTIME_WITH_SOURCES")) {
|
||||
return ProjectDescriptorWithStdlibSources.INSTANCE
|
||||
}
|
||||
else if (InTextDirectivesUtils.isDirectiveDefined(fileText, "RUNTIME_WITH_KOTLIN_TEST")) {
|
||||
return KotlinWithJdkAndRuntimeLightProjectDescriptor.INSTANCE_WITH_KOTLIN_TEST
|
||||
}
|
||||
else if (InTextDirectivesUtils.isDirectiveDefined(fileText, "RUNTIME_WITH_FULL_JDK")) {
|
||||
return KotlinWithJdkAndRuntimeLightProjectDescriptor.INSTANCE_FULL_JDK
|
||||
}
|
||||
else if (InTextDirectivesUtils.isDirectiveDefined(fileText, "RUNTIME_WITH_REFLECT")) {
|
||||
return KotlinWithJdkAndRuntimeLightProjectDescriptor.INSTANCE_WITH_REFLECT
|
||||
}
|
||||
else if (InTextDirectivesUtils.isDirectiveDefined(fileText, "RUNTIME") ||
|
||||
InTextDirectivesUtils.isDirectiveDefined(fileText, "WITH_RUNTIME")) {
|
||||
return KotlinWithJdkAndRuntimeLightProjectDescriptor.INSTANCE
|
||||
}
|
||||
else if (InTextDirectivesUtils.isDirectiveDefined(fileText, "JS")) {
|
||||
return KotlinStdJSProjectDescriptor
|
||||
}
|
||||
else if (InTextDirectivesUtils.isDirectiveDefined(fileText, "ENABLE_MULTIPLATFORM")) {
|
||||
return KotlinProjectDescriptorWithFacet.KOTLIN_STABLE_WITH_MULTIPLATFORM
|
||||
}
|
||||
}
|
||||
catch (e: IOException) {
|
||||
throw rethrow(e)
|
||||
}
|
||||
}
|
||||
return KotlinLightProjectDescriptor.INSTANCE
|
||||
}
|
||||
|
||||
protected fun isAllFilesPresentInTest(): Boolean = KotlinTestUtils.isAllFilesPresentTest(getTestName(false))
|
||||
|
||||
protected open fun fileName(): String
|
||||
= KotlinTestUtils.getTestDataFileName(this::class.java, this.name) ?: (getTestName(false) + ".kt")
|
||||
|
||||
protected fun performNotWriteEditorAction(actionId: String): Boolean {
|
||||
val dataContext = (myFixture.editor as EditorEx).dataContext
|
||||
|
||||
val managerEx = ActionManagerEx.getInstanceEx()
|
||||
val action = managerEx.getAction(actionId)
|
||||
val event = AnActionEvent(null, dataContext, ActionPlaces.UNKNOWN, Presentation(), managerEx, 0)
|
||||
|
||||
action.update(event)
|
||||
if (!event.presentation.isEnabled) {
|
||||
return false
|
||||
}
|
||||
|
||||
managerEx.fireBeforeActionPerformed(action, dataContext, event)
|
||||
action.actionPerformed(event)
|
||||
|
||||
managerEx.fireAfterActionPerformed(action, dataContext, event)
|
||||
return true
|
||||
}
|
||||
|
||||
override fun getTestDataPath(): String {
|
||||
return this::class.findAnnotation<TestMetadata>()?.value ?: super.getTestDataPath()
|
||||
}
|
||||
}
|
||||
|
||||
fun configureCompilerOptions(fileText: String, project: Project, module: Module) {
|
||||
val version = InTextDirectivesUtils.findStringWithPrefixes(fileText, "// LANGUAGE_VERSION: ")
|
||||
val jvmTarget = InTextDirectivesUtils.findStringWithPrefixes(fileText, "// JVM_TARGET: ")
|
||||
val options = InTextDirectivesUtils.findStringWithPrefixes(fileText, "// COMPILER_ARGUMENTS: ")
|
||||
|
||||
if (version != null || jvmTarget != null || options != null) {
|
||||
configureLanguageAndApiVersion(project, module, version ?: LanguageVersion.LATEST_STABLE.versionString)
|
||||
|
||||
val facetSettings = KotlinFacet.get(module)!!.configuration.settings
|
||||
|
||||
if (jvmTarget != null) {
|
||||
(facetSettings.compilerArguments as K2JVMCompilerArguments).jvmTarget = jvmTarget
|
||||
}
|
||||
|
||||
if (options != null) {
|
||||
val compilerSettings = facetSettings.compilerSettings ?: CompilerSettings().also {
|
||||
facetSettings.compilerSettings = it
|
||||
}
|
||||
compilerSettings.additionalArguments = options
|
||||
facetSettings.updateMergedArguments()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun configureLanguageAndApiVersion(
|
||||
project: Project,
|
||||
module: Module,
|
||||
languageVersion: String,
|
||||
apiVersion: String? = null
|
||||
) {
|
||||
val accessToken = WriteAction.start()
|
||||
try {
|
||||
val modelsProvider = IdeModifiableModelsProviderImpl(project)
|
||||
val facet = module.getOrCreateFacet(modelsProvider, useProjectSettings = false)
|
||||
facet.configureFacet(languageVersion, LanguageFeature.State.DISABLED, null, modelsProvider)
|
||||
if (apiVersion != null) {
|
||||
facet.configuration.settings.apiLevel = LanguageVersion.fromVersionString(apiVersion)
|
||||
}
|
||||
modelsProvider.commit()
|
||||
}
|
||||
finally {
|
||||
accessToken.finish()
|
||||
}
|
||||
}
|
||||
|
||||
fun Project.allKotlinFiles(): List<KtFile> {
|
||||
val virtualFiles = FileTypeIndex.getFiles(KotlinFileType.INSTANCE, ProjectScope.getProjectScope(this))
|
||||
return virtualFiles
|
||||
.map { PsiManager.getInstance(this).findFile(it) }
|
||||
.filterIsInstance<KtFile>()
|
||||
}
|
||||
|
||||
fun Project.findFileWithCaret() =
|
||||
allKotlinFiles().single { "<caret>" in VfsUtilCore.loadText(it.virtualFile) }
|
||||
@@ -1,118 +1,9 @@
|
||||
<idea-plugin>
|
||||
<extensions defaultExtensionNs="com.intellij">
|
||||
<externalAnnotator language="kotlin" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintExternalAnnotator"/>
|
||||
<externalAnnotator language="kotlin" implementationClass="org.jetbrains.android.inspections.lint.AndroidLintExternalAnnotator"/>
|
||||
</extensions>
|
||||
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintAddJavascriptInterface" displayName="addJavascriptInterface Called" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintAddJavascriptInterfaceInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintAllowAllHostnameVerifier" displayName="Insecure HostnameVerifier" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintAllowAllHostnameVerifierInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintAlwaysShowAction" displayName="Usage of showAsAction=always" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintAlwaysShowActionInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintAppCompatMethod" displayName="Using Wrong AppCompat Method" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintAppCompatMethodInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintAuthLeak" displayName="Code contains url auth" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintAuthLeakInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintBadHostnameVerifier" displayName="Insecure HostnameVerifier" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintBadHostnameVerifierInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintBatteryLife" displayName="Battery Life Issues" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintBatteryLifeInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintCommitPrefEdits" displayName="Missing commit() on SharedPreference editor" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintCommitPrefEditsInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintCommitTransaction" displayName="Missing commit() calls" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintCommitTransactionInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintCustomViewStyleable" displayName="Mismatched Styleable/Custom View Name" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintCustomViewStyleableInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintCutPasteId" displayName="Likely cut & paste mistakes" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintCutPasteIdInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintDefaultLocale" displayName="Implied default locale in case conversion" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintDefaultLocaleInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintDrawAllocation" displayName="Memory allocations within drawing code" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintDrawAllocationInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintEasterEgg" displayName="Code contains easter egg" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="false" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintEasterEggInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintExportedContentProvider" displayName="Content provider does not require permission" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintExportedContentProviderInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintExportedPreferenceActivity" displayName="PreferenceActivity should not be exported" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintExportedPreferenceActivityInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintExportedReceiver" displayName="Receiver does not require permission" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintExportedReceiverInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintExportedService" displayName="Exported service does not require permission" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintExportedServiceInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintFloatMath" displayName="Using FloatMath instead of Math" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintFloatMathInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintGetInstance" displayName="Cipher.getInstance with ECB" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintGetInstanceInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintGifUsage" displayName="Using .gif format for bitmaps is discouraged" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintGifUsageInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintGoogleAppIndexingApiWarning" displayName="Missing support for Google App Indexing Api" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="false" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintGoogleAppIndexingApiWarningInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintGoogleAppIndexingUrlError" displayName="URL not supported by app for Google App Indexing" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintGoogleAppIndexingUrlErrorInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintGoogleAppIndexingWarning" displayName="Missing support for Google App Indexing" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintGoogleAppIndexingWarningInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintGrantAllUris" displayName="Content provider shares everything" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintGrantAllUrisInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintHandlerLeak" displayName="Handler reference leaks" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintHandlerLeakInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintIconColors" displayName="Icon colors do not follow the recommended visual style" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintIconColorsInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintIconDensities" displayName="Icon densities validation" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintIconDensitiesInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintIconDipSize" displayName="Icon density-independent size validation" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintIconDipSizeInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintIconDuplicates" displayName="Duplicated icons under different names" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintIconDuplicatesInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintIconDuplicatesConfig" displayName="Identical bitmaps across various configurations" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintIconDuplicatesConfigInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintIconExpectedSize" displayName="Icon has incorrect size" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="false" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintIconExpectedSizeInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintIconExtension" displayName="Icon format does not match the file extension" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintIconExtensionInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintIconLauncherShape" displayName="The launcher icon shape should use a distinct silhouette" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintIconLauncherShapeInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintIconLocation" displayName="Image defined in density-independent drawable folder" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintIconLocationInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintIconMissingDensityFolder" displayName="Missing density folder" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintIconMissingDensityFolderInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintIconMixedNinePatch" displayName="Clashing PNG and 9-PNG files" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintIconMixedNinePatchInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintIconNoDpi" displayName="Icon appears in both -nodpi and dpi folders" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintIconNoDpiInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintIconXmlAndPng" displayName="Icon is specified both as .xml file and as a bitmap" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintIconXmlAndPngInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintInconsistentLayout" displayName="Inconsistent Layouts" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintInconsistentLayoutInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintInflateParams" displayName="Layout Inflation without a Parent" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintInflateParamsInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintInlinedApi" displayName="Using inlined constants on older versions" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintInlinedApiInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintInvalidUsesTagAttribute" displayName="Invalid name attribute for uses element." groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintInvalidUsesTagAttributeInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintJavascriptInterface" displayName="Missing @JavascriptInterface on methods" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintJavascriptInterfaceInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintLocalSuppress" displayName="@SuppressLint on invalid element" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintLocalSuppressInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintLogConditional" displayName="Unconditional Logging Calls" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="false" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintLogConditionalInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintLogTagMismatch" displayName="Mismatched Log Tags" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintLogTagMismatchInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintLongLogTag" displayName="Too Long Log Tags" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintLongLogTagInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintMergeRootFrame" displayName="FrameLayout can be replaced with <merge> tag" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintMergeRootFrameInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintMissingIntentFilterForMediaSearch" displayName="Missing intent-filter with action android.media.action.MEDIA_PLAY_FROM_SEARCH" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintMissingIntentFilterForMediaSearchInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintMissingMediaBrowserServiceIntentFilter" displayName="Missing intent-filter with action android.media.browse.MediaBrowserService." groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintMissingMediaBrowserServiceIntentFilterInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintMissingOnPlayFromSearch" displayName="Missing onPlayFromSearch." groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintMissingOnPlayFromSearchInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintMissingSuperCall" displayName="Missing Super Call" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintMissingSuperCallInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintNewApi" displayName="Calling new methods on older versions" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintNewApiInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintOverdraw" displayName="Overdraw: Painting regions more than once" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintOverdrawInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintOverride" displayName="Method conflicts with new inherited method" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintOverrideInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintOverrideAbstract" displayName="Not overriding abstract methods on older platforms" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintOverrideAbstractInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintPackageManagerGetSignatures" displayName="Potential Multiple Certificate Exploit" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintPackageManagerGetSignaturesInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintParcelClassLoader" displayName="Default Parcel Class Loader" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintParcelClassLoaderInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintParcelCreator" displayName="Missing Parcelable CREATOR field" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintParcelCreatorInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintPendingBindings" displayName="Missing Pending Bindings" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintPendingBindingsInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintPluralsCandidate" displayName="Potential Plurals" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintPluralsCandidateInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintPrivateResource" displayName="Using private resources" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintPrivateResourceInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintRecycle" displayName="Missing recycle() calls" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintRecycleInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintRecyclerView" displayName="RecyclerView Problems" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintRecyclerViewInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintRegistered" displayName="Class is not registered in the manifest" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintRegisteredInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintRequiredSize" displayName="Missing layout_width or layout_height attributes" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintRequiredSizeInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintRtlCompat" displayName="Right-to-left text compatibility issues" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintRtlCompatInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintRtlEnabled" displayName="Using RTL attributes without enabling RTL support" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintRtlEnabledInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintRtlHardcoded" displayName="Using left/right instead of start/end attributes" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintRtlHardcodedInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintRtlSymmetry" displayName="Padding and margin symmetry" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintRtlSymmetryInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintSdCardPath" displayName="Hardcoded reference to /sdcard" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintSdCardPathInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintSecureRandom" displayName="Using a fixed seed with SecureRandom" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintSecureRandomInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintServiceCast" displayName="Wrong system service casts" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintServiceCastInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintSetJavaScriptEnabled" displayName="Using setJavaScriptEnabled" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintSetJavaScriptEnabledInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintSetTextI18n" displayName="TextView Internationalization" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintSetTextI18nInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintSetWorldReadable" displayName="File.setReadable() used to make file world-readable" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintSetWorldReadableInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintSetWorldWritable" displayName="File.setWritable() used to make file world-writable" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintSetWorldWritableInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintShiftFlags" displayName="Dangerous Flag Constant Declaration" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintShiftFlagsInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintShortAlarm" displayName="Short or Frequent Alarm" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintShortAlarmInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintShowToast" displayName="Toast created but not shown" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintShowToastInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintSimpleDateFormat" displayName="Implied locale in date format" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintSimpleDateFormatInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintSQLiteString" displayName="Using STRING instead of TEXT" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintSQLiteStringInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintSSLCertificateSocketFactoryCreateSocket" displayName="Insecure call to SSLCertificateSocketFactory.createSocket()" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintSSLCertificateSocketFactoryCreateSocketInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintSSLCertificateSocketFactoryGetInsecure" displayName="Call to SSLCertificateSocketFactory.getInsecure()" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintSSLCertificateSocketFactoryGetInsecureInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintStopShip" displayName="Code contains STOPSHIP marker" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="false" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintStopShipInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintStringFormatCount" displayName="Formatting argument types incomplete or inconsistent" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintStringFormatCountInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintStringFormatInvalid" displayName="Invalid format string" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintStringFormatInvalidInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintStringFormatMatches" displayName="String.format string doesn't match the XML format string" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintStringFormatMatchesInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintSupportAnnotationUsage" displayName="Incorrect support annotation usage" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintSupportAnnotationUsageInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintSuspiciousImport" displayName="'import android.R' statement" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintSuspiciousImportInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintSwitchIntDef" displayName="Missing @IntDef in Switch" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintSwitchIntDefInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintTrustAllX509TrustManager" displayName="Insecure TLS/SSL trust manager" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintTrustAllX509TrustManagerInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintUniqueConstants" displayName="Overlapping Enumeration Constants" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintUniqueConstantsInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintUnlocalizedSms" displayName="SMS phone number missing country code" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintUnlocalizedSmsInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintUnprotectedSMSBroadcastReceiver" displayName="Unprotected SMS BroadcastReceiver" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintUnprotectedSMSBroadcastReceiverInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintUnsafeDynamicallyLoadedCode" displayName="load used to dynamically load code" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintUnsafeDynamicallyLoadedCodeInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintUnsafeNativeCodeLocation" displayName="Native code outside library directory" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintUnsafeNativeCodeLocationInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintUnsafeProtectedBroadcastReceiver" displayName="Unsafe Protected BroadcastReceiver" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintUnsafeProtectedBroadcastReceiverInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintUnusedAttribute" displayName="Attribute unused on older versions" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintUnusedAttributeInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintUseSparseArrays" displayName="HashMap can be replaced with SparseArray" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintUseSparseArraysInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintUseValueOf" displayName="Should use valueOf instead of new" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintUseValueOfInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintValidFragment" displayName="Fragment not instantiatable" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintValidFragmentInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintViewConstructor" displayName="Missing View constructors for XML inflation" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintViewConstructorInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintViewHolder" displayName="View Holder Candidates" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintViewHolderInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintViewTag" displayName="Tagged object leaks" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintViewTagInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintWorldReadableFiles" displayName="openFileOutput() or similar call passing MODE_WORLD_READABLE" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintWorldReadableFilesInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintWorldWriteableFiles" displayName="openFileOutput() or similar call passing MODE_WORLD_WRITEABLE" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintWorldWriteableFilesInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintWrongCall" displayName="Using wrong draw/layout method" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintWrongCallInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintWrongViewCast" displayName="Mismatched view type" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintWrongViewCastInspection"/>
|
||||
|
||||
<codeInspection.InspectionExtension implementation="org.jetbrains.android.inspections.klint.AndroidInspectionExtensionsFactory"/>
|
||||
<extensions defaultExtensionNs="org.jetbrains.uast">
|
||||
<uastLanguagePlugin implementation="org.jetbrains.uast.kotlin.KotlinUastLanguagePlugin"/>
|
||||
</extensions>
|
||||
</idea-plugin>
|
||||
@@ -0,0 +1,118 @@
|
||||
<idea-plugin>
|
||||
<extensions defaultExtensionNs="com.intellij">
|
||||
<externalAnnotator language="kotlin" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintExternalAnnotator"/>
|
||||
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintAddJavascriptInterface" displayName="addJavascriptInterface Called" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintAddJavascriptInterfaceInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintAllowAllHostnameVerifier" displayName="Insecure HostnameVerifier" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintAllowAllHostnameVerifierInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintAlwaysShowAction" displayName="Usage of showAsAction=always" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintAlwaysShowActionInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintAppCompatMethod" displayName="Using Wrong AppCompat Method" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintAppCompatMethodInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintAuthLeak" displayName="Code contains url auth" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintAuthLeakInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintBadHostnameVerifier" displayName="Insecure HostnameVerifier" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintBadHostnameVerifierInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintBatteryLife" displayName="Battery Life Issues" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintBatteryLifeInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintCommitPrefEdits" displayName="Missing commit() on SharedPreference editor" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintCommitPrefEditsInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintCommitTransaction" displayName="Missing commit() calls" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintCommitTransactionInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintCustomViewStyleable" displayName="Mismatched Styleable/Custom View Name" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintCustomViewStyleableInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintCutPasteId" displayName="Likely cut & paste mistakes" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintCutPasteIdInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintDefaultLocale" displayName="Implied default locale in case conversion" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintDefaultLocaleInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintDrawAllocation" displayName="Memory allocations within drawing code" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintDrawAllocationInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintEasterEgg" displayName="Code contains easter egg" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="false" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintEasterEggInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintExportedContentProvider" displayName="Content provider does not require permission" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintExportedContentProviderInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintExportedPreferenceActivity" displayName="PreferenceActivity should not be exported" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintExportedPreferenceActivityInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintExportedReceiver" displayName="Receiver does not require permission" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintExportedReceiverInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintExportedService" displayName="Exported service does not require permission" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintExportedServiceInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintFloatMath" displayName="Using FloatMath instead of Math" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintFloatMathInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintGetInstance" displayName="Cipher.getInstance with ECB" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintGetInstanceInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintGifUsage" displayName="Using .gif format for bitmaps is discouraged" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintGifUsageInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintGoogleAppIndexingApiWarning" displayName="Missing support for Google App Indexing Api" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="false" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintGoogleAppIndexingApiWarningInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintGoogleAppIndexingUrlError" displayName="URL not supported by app for Google App Indexing" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintGoogleAppIndexingUrlErrorInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintGoogleAppIndexingWarning" displayName="Missing support for Google App Indexing" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintGoogleAppIndexingWarningInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintGrantAllUris" displayName="Content provider shares everything" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintGrantAllUrisInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintHandlerLeak" displayName="Handler reference leaks" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintHandlerLeakInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintIconColors" displayName="Icon colors do not follow the recommended visual style" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintIconColorsInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintIconDensities" displayName="Icon densities validation" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintIconDensitiesInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintIconDipSize" displayName="Icon density-independent size validation" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintIconDipSizeInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintIconDuplicates" displayName="Duplicated icons under different names" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintIconDuplicatesInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintIconDuplicatesConfig" displayName="Identical bitmaps across various configurations" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintIconDuplicatesConfigInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintIconExpectedSize" displayName="Icon has incorrect size" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="false" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintIconExpectedSizeInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintIconExtension" displayName="Icon format does not match the file extension" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintIconExtensionInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintIconLauncherShape" displayName="The launcher icon shape should use a distinct silhouette" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintIconLauncherShapeInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintIconLocation" displayName="Image defined in density-independent drawable folder" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintIconLocationInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintIconMissingDensityFolder" displayName="Missing density folder" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintIconMissingDensityFolderInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintIconMixedNinePatch" displayName="Clashing PNG and 9-PNG files" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintIconMixedNinePatchInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintIconNoDpi" displayName="Icon appears in both -nodpi and dpi folders" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintIconNoDpiInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintIconXmlAndPng" displayName="Icon is specified both as .xml file and as a bitmap" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintIconXmlAndPngInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintInconsistentLayout" displayName="Inconsistent Layouts" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintInconsistentLayoutInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintInflateParams" displayName="Layout Inflation without a Parent" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintInflateParamsInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintInlinedApi" displayName="Using inlined constants on older versions" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintInlinedApiInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintInvalidUsesTagAttribute" displayName="Invalid name attribute for uses element." groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintInvalidUsesTagAttributeInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintJavascriptInterface" displayName="Missing @JavascriptInterface on methods" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintJavascriptInterfaceInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintLocalSuppress" displayName="@SuppressLint on invalid element" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintLocalSuppressInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintLogConditional" displayName="Unconditional Logging Calls" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="false" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintLogConditionalInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintLogTagMismatch" displayName="Mismatched Log Tags" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintLogTagMismatchInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintLongLogTag" displayName="Too Long Log Tags" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintLongLogTagInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintMergeRootFrame" displayName="FrameLayout can be replaced with <merge> tag" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintMergeRootFrameInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintMissingIntentFilterForMediaSearch" displayName="Missing intent-filter with action android.media.action.MEDIA_PLAY_FROM_SEARCH" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintMissingIntentFilterForMediaSearchInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintMissingMediaBrowserServiceIntentFilter" displayName="Missing intent-filter with action android.media.browse.MediaBrowserService." groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintMissingMediaBrowserServiceIntentFilterInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintMissingOnPlayFromSearch" displayName="Missing onPlayFromSearch." groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintMissingOnPlayFromSearchInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintMissingSuperCall" displayName="Missing Super Call" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintMissingSuperCallInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintNewApi" displayName="Calling new methods on older versions" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintNewApiInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintOverdraw" displayName="Overdraw: Painting regions more than once" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintOverdrawInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintOverride" displayName="Method conflicts with new inherited method" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintOverrideInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintOverrideAbstract" displayName="Not overriding abstract methods on older platforms" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintOverrideAbstractInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintPackageManagerGetSignatures" displayName="Potential Multiple Certificate Exploit" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintPackageManagerGetSignaturesInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintParcelClassLoader" displayName="Default Parcel Class Loader" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintParcelClassLoaderInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintParcelCreator" displayName="Missing Parcelable CREATOR field" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintParcelCreatorInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintPendingBindings" displayName="Missing Pending Bindings" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintPendingBindingsInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintPluralsCandidate" displayName="Potential Plurals" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintPluralsCandidateInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintPrivateResource" displayName="Using private resources" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintPrivateResourceInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintRecycle" displayName="Missing recycle() calls" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintRecycleInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintRecyclerView" displayName="RecyclerView Problems" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintRecyclerViewInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintRegistered" displayName="Class is not registered in the manifest" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintRegisteredInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintRequiredSize" displayName="Missing layout_width or layout_height attributes" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintRequiredSizeInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintRtlCompat" displayName="Right-to-left text compatibility issues" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintRtlCompatInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintRtlEnabled" displayName="Using RTL attributes without enabling RTL support" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintRtlEnabledInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintRtlHardcoded" displayName="Using left/right instead of start/end attributes" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintRtlHardcodedInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintRtlSymmetry" displayName="Padding and margin symmetry" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintRtlSymmetryInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintSdCardPath" displayName="Hardcoded reference to /sdcard" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintSdCardPathInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintSecureRandom" displayName="Using a fixed seed with SecureRandom" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintSecureRandomInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintServiceCast" displayName="Wrong system service casts" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintServiceCastInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintSetJavaScriptEnabled" displayName="Using setJavaScriptEnabled" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintSetJavaScriptEnabledInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintSetTextI18n" displayName="TextView Internationalization" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintSetTextI18nInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintSetWorldReadable" displayName="File.setReadable() used to make file world-readable" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintSetWorldReadableInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintSetWorldWritable" displayName="File.setWritable() used to make file world-writable" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintSetWorldWritableInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintShiftFlags" displayName="Dangerous Flag Constant Declaration" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintShiftFlagsInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintShortAlarm" displayName="Short or Frequent Alarm" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintShortAlarmInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintShowToast" displayName="Toast created but not shown" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintShowToastInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintSimpleDateFormat" displayName="Implied locale in date format" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintSimpleDateFormatInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintSQLiteString" displayName="Using STRING instead of TEXT" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintSQLiteStringInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintSSLCertificateSocketFactoryCreateSocket" displayName="Insecure call to SSLCertificateSocketFactory.createSocket()" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintSSLCertificateSocketFactoryCreateSocketInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintSSLCertificateSocketFactoryGetInsecure" displayName="Call to SSLCertificateSocketFactory.getInsecure()" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintSSLCertificateSocketFactoryGetInsecureInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintStopShip" displayName="Code contains STOPSHIP marker" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="false" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintStopShipInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintStringFormatCount" displayName="Formatting argument types incomplete or inconsistent" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintStringFormatCountInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintStringFormatInvalid" displayName="Invalid format string" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintStringFormatInvalidInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintStringFormatMatches" displayName="String.format string doesn't match the XML format string" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintStringFormatMatchesInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintSupportAnnotationUsage" displayName="Incorrect support annotation usage" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintSupportAnnotationUsageInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintSuspiciousImport" displayName="'import android.R' statement" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintSuspiciousImportInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintSwitchIntDef" displayName="Missing @IntDef in Switch" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintSwitchIntDefInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintTrustAllX509TrustManager" displayName="Insecure TLS/SSL trust manager" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintTrustAllX509TrustManagerInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintUniqueConstants" displayName="Overlapping Enumeration Constants" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintUniqueConstantsInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintUnlocalizedSms" displayName="SMS phone number missing country code" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintUnlocalizedSmsInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintUnprotectedSMSBroadcastReceiver" displayName="Unprotected SMS BroadcastReceiver" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintUnprotectedSMSBroadcastReceiverInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintUnsafeDynamicallyLoadedCode" displayName="load used to dynamically load code" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintUnsafeDynamicallyLoadedCodeInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintUnsafeNativeCodeLocation" displayName="Native code outside library directory" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintUnsafeNativeCodeLocationInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintUnsafeProtectedBroadcastReceiver" displayName="Unsafe Protected BroadcastReceiver" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintUnsafeProtectedBroadcastReceiverInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintUnusedAttribute" displayName="Attribute unused on older versions" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintUnusedAttributeInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintUseSparseArrays" displayName="HashMap can be replaced with SparseArray" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintUseSparseArraysInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintUseValueOf" displayName="Should use valueOf instead of new" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintUseValueOfInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintValidFragment" displayName="Fragment not instantiatable" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintValidFragmentInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintViewConstructor" displayName="Missing View constructors for XML inflation" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintViewConstructorInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintViewHolder" displayName="View Holder Candidates" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintViewHolderInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintViewTag" displayName="Tagged object leaks" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintViewTagInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintWorldReadableFiles" displayName="openFileOutput() or similar call passing MODE_WORLD_READABLE" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintWorldReadableFilesInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintWorldWriteableFiles" displayName="openFileOutput() or similar call passing MODE_WORLD_WRITEABLE" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintWorldWriteableFilesInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintWrongCall" displayName="Using wrong draw/layout method" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintWrongCallInspection"/>
|
||||
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintWrongViewCast" displayName="Mismatched view type" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintWrongViewCastInspection"/>
|
||||
|
||||
<codeInspection.InspectionExtension implementation="org.jetbrains.android.inspections.klint.AndroidInspectionExtensionsFactory"/>
|
||||
</extensions>
|
||||
</idea-plugin>
|
||||
@@ -100,6 +100,10 @@
|
||||
<highlighterExtension implementation="org.jetbrains.kotlin.android.AndroidHighlighterExtension"/>
|
||||
</extensions>
|
||||
|
||||
<extensions defaultExtensionNs="org.jetbrains.android">
|
||||
<androidLintQuickFixProvider implementation="org.jetbrains.kotlin.android.quickfix.KotlinAndroidQuickFixProvider" />
|
||||
</extensions>
|
||||
|
||||
<extensions defaultExtensionNs="org.jetbrains.kotlin.android.model">
|
||||
<androidModuleInfoProvider implementation="org.jetbrains.kotlin.android.model.impl.AndroidModuleInfoProviderImpl"/>
|
||||
</extensions>
|
||||
|
||||
@@ -0,0 +1,106 @@
|
||||
<idea-plugin xmlns:xi="http://www.w3.org/2001/XInclude">
|
||||
<xi:include href="android-lint.xml" xpointer="xpointer(/idea-plugin/*)"/>
|
||||
|
||||
<extensions defaultExtensionNs="com.intellij">
|
||||
<gotoDeclarationHandler implementation="org.jetbrains.kotlin.android.navigation.KotlinAndroidGotoDeclarationHandler"/>
|
||||
|
||||
<moduleService serviceInterface="org.jetbrains.kotlin.android.synthetic.res.AndroidLayoutXmlFileManager"
|
||||
serviceImplementation="org.jetbrains.kotlin.android.synthetic.idea.res.IDEAndroidLayoutXmlFileManager"/>
|
||||
<psi.treeChangePreprocessor implementation="org.jetbrains.kotlin.android.synthetic.idea.AndroidPsiTreeChangePreprocessor"/>
|
||||
<errorHandler implementation="org.jetbrains.kotlin.android.synthetic.idea.AndroidExtensionsReportSubmitter"/>
|
||||
<gotoDeclarationHandler implementation="org.jetbrains.kotlin.android.synthetic.idea.AndroidGotoDeclarationHandler"/>
|
||||
|
||||
<localInspection implementationClass="org.jetbrains.kotlin.android.inspection.IllegalIdentifierInspection"
|
||||
displayName="Illegal Android Identifier"
|
||||
groupName="Kotlin Android"
|
||||
enabledByDefault="true"
|
||||
level="ERROR"/>
|
||||
|
||||
<localInspection implementationClass="org.jetbrains.kotlin.android.inspection.TypeParameterFindViewByIdInspection"
|
||||
displayName="Cast can be converted to findViewById with type parameter"
|
||||
groupName="Kotlin Android"
|
||||
enabledByDefault="true"
|
||||
cleanupTool="true"
|
||||
level="WEAK WARNING"
|
||||
language="kotlin" />
|
||||
|
||||
<codeInsight.lineMarkerProvider language="kotlin" implementationClass="org.jetbrains.kotlin.android.KotlinAndroidLineMarkerProvider"/>
|
||||
|
||||
<intentionAction>
|
||||
<className>org.jetbrains.kotlin.android.intention.KotlinAndroidAddStringResource</className>
|
||||
<category>Kotlin Android</category>
|
||||
</intentionAction>
|
||||
|
||||
<intentionAction>
|
||||
<className>org.jetbrains.kotlin.android.intention.AddActivityToManifest</className>
|
||||
<category>Kotlin Android</category>
|
||||
</intentionAction>
|
||||
|
||||
<intentionAction>
|
||||
<className>org.jetbrains.kotlin.android.intention.AddServiceToManifest</className>
|
||||
<category>Kotlin Android</category>
|
||||
</intentionAction>
|
||||
|
||||
<intentionAction>
|
||||
<className>org.jetbrains.kotlin.android.intention.AddBroadcastReceiverToManifest</className>
|
||||
<category>Kotlin Android</category>
|
||||
</intentionAction>
|
||||
<intentionAction>
|
||||
<className>org.jetbrains.kotlin.android.intention.ImplementParcelableAction</className>
|
||||
<category>Kotlin Android</category>
|
||||
</intentionAction>
|
||||
|
||||
<intentionAction>
|
||||
<className>org.jetbrains.kotlin.android.intention.RemoveParcelableAction</className>
|
||||
<category>Kotlin Android</category>
|
||||
</intentionAction>
|
||||
|
||||
<intentionAction>
|
||||
<className>org.jetbrains.kotlin.android.intention.RedoParcelableAction</className>
|
||||
<category>Kotlin Android</category>
|
||||
</intentionAction>
|
||||
|
||||
<codeInsight.unresolvedReferenceQuickFixProvider
|
||||
implementation="org.jetbrains.kotlin.android.inspection.KotlinAndroidResourceQuickFixProvider"/>
|
||||
|
||||
<lang.foldingBuilder language="kotlin" implementationClass="org.jetbrains.kotlin.android.folding.ResourceFoldingBuilder" />
|
||||
|
||||
<annotator language="kotlin" implementationClass="org.jetbrains.kotlin.android.AndroidResourceReferenceAnnotator" />
|
||||
|
||||
<referencesSearch implementation="org.jetbrains.kotlin.AndroidExtensionsReferenceSearchExecutor"/>
|
||||
|
||||
<externalProjectDataService implementation="org.jetbrains.kotlin.android.configure.KotlinAndroidGradleLibraryDataService"/>
|
||||
</extensions>
|
||||
|
||||
<extensions defaultExtensionNs="org.jetbrains.plugins.gradle">
|
||||
<projectResolve implementation="org.jetbrains.kotlin.android.synthetic.idea.AndroidExtensionsProjectResolverExtension"/>
|
||||
</extensions>
|
||||
|
||||
<extensions defaultExtensionNs="org.jetbrains.kotlin">
|
||||
<expressionCodegenExtension implementation="org.jetbrains.kotlin.android.synthetic.idea.IDEAndroidExtensionsExpressionCodegenExtension"/>
|
||||
<storageComponentContainerContributor implementation="org.jetbrains.kotlin.android.synthetic.AndroidExtensionPropertiesComponentContainerContributor"/>
|
||||
<classBuilderFactoryInterceptorExtension implementation="org.jetbrains.kotlin.android.synthetic.idea.IDEAndroidOnDestroyClassBuilderInterceptorExtension"/>
|
||||
<packageFragmentProviderExtension implementation="org.jetbrains.kotlin.android.synthetic.idea.res.IDEAndroidPackageFragmentProviderExtension"/>
|
||||
<simpleNameReferenceExtension implementation="org.jetbrains.kotlin.android.synthetic.idea.AndroidSimpleNameReferenceExtension"/>
|
||||
<kotlinIndicesHelperExtension implementation="org.jetbrains.kotlin.android.synthetic.idea.AndroidIndicesHelperExtension"/>
|
||||
<gradleProjectImportHandler implementation="org.jetbrains.kotlin.android.synthetic.idea.AndroidExtensionsGradleImportHandler"/>
|
||||
|
||||
<quickFixContributor implementation="org.jetbrains.kotlin.android.quickfix.AndroidQuickFixRegistrar"/>
|
||||
<projectConfigurator implementation="org.jetbrains.kotlin.android.configure.KotlinAndroidGradleModuleConfigurator"/>
|
||||
<gradleModelFacade implementation="org.jetbrains.kotlin.android.configure.AndroidGradleModelFacade"/>
|
||||
<completionInformationProvider implementation="org.jetbrains.kotlin.AndroidExtensionsCompletionInformationProvider" />
|
||||
|
||||
<expressionCodegenExtension implementation="org.jetbrains.kotlin.android.parcel.IDEParcelableCodegenExtension"/>
|
||||
<syntheticResolveExtension implementation="org.jetbrains.kotlin.android.parcel.IDEParcelableResolveExtension"/>
|
||||
<classBuilderFactoryInterceptorExtension implementation="org.jetbrains.kotlin.android.synthetic.codegen.ParcelableClinitClassBuilderInterceptorExtension"/>
|
||||
<quickFixContributor implementation="org.jetbrains.kotlin.android.parcel.quickfixes.ParcelableQuickFixContributor"/>
|
||||
|
||||
<androidDexer implementation="org.jetbrains.kotlin.android.debugger.AndroidDexerImpl"/>
|
||||
|
||||
<highlighterExtension implementation="org.jetbrains.kotlin.android.AndroidHighlighterExtension"/>
|
||||
</extensions>
|
||||
|
||||
<extensions defaultExtensionNs="org.jetbrains.kotlin.android.model">
|
||||
<androidModuleInfoProvider implementation="org.jetbrains.kotlin.android.model.impl.AndroidModuleInfoProviderImpl"/>
|
||||
</extensions>
|
||||
</idea-plugin>
|
||||
@@ -6,7 +6,7 @@
|
||||
<version>@snapshot@</version>
|
||||
<vendor url="http://www.jetbrains.com">JetBrains</vendor>
|
||||
|
||||
<idea-version since-build="173.1" until-build="173.*"/>
|
||||
<idea-version since-build="181.1" until-build="181.*"/>
|
||||
|
||||
<depends>com.intellij.modules.platform</depends>
|
||||
<depends>com.intellij.modules.remoteServers</depends>
|
||||
@@ -355,7 +355,6 @@
|
||||
<internalFileTemplate name="Kotlin Class"/>
|
||||
<internalFileTemplate name="Kotlin Enum"/>
|
||||
<internalFileTemplate name="Kotlin Interface"/>
|
||||
<internalFileTemplate name="Kotlin Script"/>
|
||||
|
||||
<gotoSymbolContributor implementation="org.jetbrains.kotlin.idea.goto.KotlinGotoSymbolContributor"/>
|
||||
<gotoClassContributor implementation="org.jetbrains.kotlin.idea.goto.KotlinGotoClassContributor"/>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -98,7 +98,7 @@ class JavaToKotlinAction : AnAction() {
|
||||
var converterResult: JavaToKotlinConverter.FilesResult? = null
|
||||
fun convert() {
|
||||
val converter = JavaToKotlinConverter(project, ConverterSettings.defaultSettings, IdeaJavaToKotlinServices)
|
||||
converterResult = converter.filesToKotlin(javaFiles, J2kPostProcessor(formatCode = true), ProgressManager.getInstance().progressIndicator)
|
||||
converterResult = converter.filesToKotlin(javaFiles, J2kPostProcessor(formatCode = true), ProgressManager.getInstance().progressIndicator!!)
|
||||
}
|
||||
|
||||
|
||||
@@ -117,7 +117,7 @@ class JavaToKotlinAction : AnAction() {
|
||||
ProgressManager.getInstance().runProcessWithProgressSynchronously(
|
||||
{
|
||||
runReadAction {
|
||||
externalCodeUpdate = converterResult!!.externalCodeProcessing!!.prepareWriteOperation(ProgressManager.getInstance().progressIndicator)
|
||||
externalCodeUpdate = converterResult!!.externalCodeProcessing!!.prepareWriteOperation(ProgressManager.getInstance().progressIndicator!!)
|
||||
}
|
||||
},
|
||||
title,
|
||||
|
||||
@@ -0,0 +1,216 @@
|
||||
/*
|
||||
* 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.actions
|
||||
|
||||
import com.intellij.codeInsight.navigation.NavigationUtil
|
||||
import com.intellij.ide.scratch.ScratchFileService
|
||||
import com.intellij.ide.scratch.ScratchRootType
|
||||
import com.intellij.openapi.actionSystem.AnAction
|
||||
import com.intellij.openapi.actionSystem.AnActionEvent
|
||||
import com.intellij.openapi.actionSystem.CommonDataKeys
|
||||
import com.intellij.openapi.command.CommandProcessor
|
||||
import com.intellij.openapi.fileEditor.FileDocumentManager
|
||||
import com.intellij.openapi.fileEditor.FileEditorManager
|
||||
import com.intellij.openapi.progress.ProgressManager
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.ui.Messages
|
||||
import com.intellij.openapi.ui.ex.MessagesEx
|
||||
import com.intellij.openapi.vfs.VfsUtilCore
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import com.intellij.openapi.vfs.VirtualFileVisitor
|
||||
import com.intellij.psi.PsiDocumentManager
|
||||
import com.intellij.psi.PsiErrorElement
|
||||
import com.intellij.psi.PsiJavaFile
|
||||
import com.intellij.psi.PsiManager
|
||||
import com.intellij.psi.util.PsiTreeUtil
|
||||
import org.jetbrains.kotlin.idea.KotlinFileType
|
||||
import org.jetbrains.kotlin.idea.j2k.IdeaJavaToKotlinServices
|
||||
import org.jetbrains.kotlin.idea.j2k.J2kPostProcessor
|
||||
import org.jetbrains.kotlin.idea.refactoring.toPsiFile
|
||||
import org.jetbrains.kotlin.idea.util.application.executeWriteCommand
|
||||
import org.jetbrains.kotlin.idea.util.application.runReadAction
|
||||
import org.jetbrains.kotlin.j2k.ConverterSettings
|
||||
import org.jetbrains.kotlin.j2k.JavaToKotlinConverter
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
import java.util.*
|
||||
|
||||
class JavaToKotlinAction : AnAction() {
|
||||
companion object {
|
||||
private fun uniqueKotlinFileName(javaFile: VirtualFile): String {
|
||||
val ioFile = File(javaFile.path.replace('/', File.separatorChar))
|
||||
|
||||
var i = 0
|
||||
while (true) {
|
||||
val fileName = javaFile.nameWithoutExtension + (if (i > 0) i else "") + ".kt"
|
||||
if (!ioFile.resolveSibling(fileName).exists()) return fileName
|
||||
i++
|
||||
}
|
||||
}
|
||||
|
||||
val title = "Convert Java to Kotlin"
|
||||
|
||||
private fun saveResults(javaFiles: List<PsiJavaFile>, convertedTexts: List<String>): List<VirtualFile> {
|
||||
val result = ArrayList<VirtualFile>()
|
||||
for ((psiFile, text) in javaFiles.zip(convertedTexts)) {
|
||||
try {
|
||||
val document = PsiDocumentManager.getInstance(psiFile.project).getDocument(psiFile)
|
||||
if (document == null) {
|
||||
MessagesEx.error(psiFile.project, "Failed to save conversion result: couldn't find document for " + psiFile.name).showLater()
|
||||
continue
|
||||
}
|
||||
document.replaceString(0, document.textLength, text)
|
||||
FileDocumentManager.getInstance().saveDocument(document)
|
||||
|
||||
val virtualFile = psiFile.virtualFile
|
||||
if (ScratchRootType.getInstance().containsFile(virtualFile)) {
|
||||
val mapping = ScratchFileService.getInstance().scratchesMapping
|
||||
mapping.setMapping(virtualFile, KotlinFileType.INSTANCE.language)
|
||||
}
|
||||
else {
|
||||
val fileName = uniqueKotlinFileName(virtualFile)
|
||||
virtualFile.rename(this, fileName)
|
||||
}
|
||||
}
|
||||
catch (e: IOException) {
|
||||
MessagesEx.error(psiFile.project, e.message ?: "").showLater()
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
fun convertFiles(javaFiles: List<PsiJavaFile>, project: Project, enableExternalCodeProcessing: Boolean = true): List<KtFile> {
|
||||
var converterResult: JavaToKotlinConverter.FilesResult? = null
|
||||
fun convert() {
|
||||
val converter = JavaToKotlinConverter(project, ConverterSettings.defaultSettings, IdeaJavaToKotlinServices)
|
||||
converterResult = converter.filesToKotlin(javaFiles, J2kPostProcessor(formatCode = true), ProgressManager.getInstance().progressIndicator)
|
||||
}
|
||||
|
||||
|
||||
if (!ProgressManager.getInstance().runProcessWithProgressSynchronously(
|
||||
::convert,
|
||||
title,
|
||||
true,
|
||||
project)) return emptyList()
|
||||
|
||||
|
||||
var externalCodeUpdate: (() -> Unit)? = null
|
||||
|
||||
if (enableExternalCodeProcessing && converterResult!!.externalCodeProcessing != null) {
|
||||
val question = "Some code in the rest of your project may require corrections after performing this conversion. Do you want to find such code and correct it too?"
|
||||
if (Messages.showOkCancelDialog(project, question, title, Messages.getQuestionIcon()) == Messages.OK) {
|
||||
ProgressManager.getInstance().runProcessWithProgressSynchronously(
|
||||
{
|
||||
runReadAction {
|
||||
externalCodeUpdate = converterResult!!.externalCodeProcessing!!.prepareWriteOperation(ProgressManager.getInstance().progressIndicator)
|
||||
}
|
||||
},
|
||||
title,
|
||||
true,
|
||||
project)
|
||||
}
|
||||
}
|
||||
|
||||
return project.executeWriteCommand("Convert files from Java to Kotlin", null) {
|
||||
CommandProcessor.getInstance().markCurrentCommandAsGlobal(project)
|
||||
|
||||
val newFiles = saveResults(javaFiles, converterResult!!.results)
|
||||
|
||||
externalCodeUpdate?.invoke()
|
||||
|
||||
PsiDocumentManager.getInstance(project).commitAllDocuments()
|
||||
|
||||
newFiles.singleOrNull()?.let {
|
||||
FileEditorManager.getInstance(project).openFile(it, true)
|
||||
}
|
||||
|
||||
newFiles.map { it.toPsiFile(project) as KtFile }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun actionPerformed(e: AnActionEvent) {
|
||||
val javaFiles = selectedJavaFiles(e).filter { it.isWritable }.toList()
|
||||
val project = CommonDataKeys.PROJECT.getData(e.dataContext)!!
|
||||
|
||||
val firstSyntaxError = javaFiles.asSequence().map { PsiTreeUtil.findChildOfType(it, PsiErrorElement::class.java) }.firstOrNull()
|
||||
|
||||
if (firstSyntaxError != null) {
|
||||
val count = javaFiles.filter { PsiTreeUtil.hasErrorElements(it) }.count()
|
||||
val question = firstSyntaxError.containingFile.name +
|
||||
(if (count > 1) " and ${count - 1} more Java files" else " file") +
|
||||
" contain syntax errors, the conversion result may be incorrect"
|
||||
|
||||
val okText = "Investigate Errors"
|
||||
val cancelText = "Proceed with Conversion"
|
||||
if (Messages.showOkCancelDialog(
|
||||
project,
|
||||
question,
|
||||
title,
|
||||
okText,
|
||||
cancelText,
|
||||
Messages.getWarningIcon()
|
||||
) == Messages.OK) {
|
||||
NavigationUtil.activateFileWithPsiElement(firstSyntaxError.navigationElement)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
convertFiles(javaFiles, project)
|
||||
}
|
||||
|
||||
override fun update(e: AnActionEvent) {
|
||||
val virtualFiles = e.getData(CommonDataKeys.VIRTUAL_FILE_ARRAY) ?: return
|
||||
val project = e.project ?: return
|
||||
|
||||
e.presentation.isEnabled = isAnyJavaFileSelected(project, virtualFiles)
|
||||
}
|
||||
|
||||
private fun isAnyJavaFileSelected(project: Project, files: Array<VirtualFile>): Boolean {
|
||||
val manager = PsiManager.getInstance(project)
|
||||
|
||||
if (files.any { manager.findFile(it) is PsiJavaFile && it.isWritable }) return true
|
||||
return files.any { it.isDirectory && isAnyJavaFileSelected(project, it.children) }
|
||||
}
|
||||
|
||||
private fun selectedJavaFiles(e: AnActionEvent): Sequence<PsiJavaFile> {
|
||||
val virtualFiles = e.getData(CommonDataKeys.VIRTUAL_FILE_ARRAY) ?: return sequenceOf()
|
||||
val project = e.project ?: return sequenceOf()
|
||||
return allJavaFiles(virtualFiles, project)
|
||||
}
|
||||
|
||||
private fun allJavaFiles(filesOrDirs: Array<VirtualFile>, project: Project): Sequence<PsiJavaFile> {
|
||||
val manager = PsiManager.getInstance(project)
|
||||
return allFiles(filesOrDirs)
|
||||
.asSequence()
|
||||
.mapNotNull { manager.findFile(it) as? PsiJavaFile }
|
||||
}
|
||||
|
||||
private fun allFiles(filesOrDirs: Array<VirtualFile>): Collection<VirtualFile> {
|
||||
val result = ArrayList<VirtualFile>()
|
||||
for (file in filesOrDirs) {
|
||||
VfsUtilCore.visitChildrenRecursively(file, object : VirtualFileVisitor<Unit>() {
|
||||
override fun visitFile(file: VirtualFile): Boolean {
|
||||
result.add(file)
|
||||
return true
|
||||
}
|
||||
})
|
||||
}
|
||||
return result
|
||||
}
|
||||
}
|
||||
@@ -25,7 +25,7 @@ import org.jetbrains.kotlin.lexer.KtTokens
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
|
||||
class KotlinStringTemplateBackspaceHandler : BackspaceHandlerDelegate() {
|
||||
override fun beforeCharDeleted(c: Char, file: PsiFile?, editor: Editor?) {
|
||||
override fun beforeCharDeleted(c: Char, file: PsiFile, editor: Editor) {
|
||||
if (c != '{' || file !is KtFile || !CodeInsightSettings.getInstance().AUTOINSERT_PAIR_BRACKET) return
|
||||
|
||||
val offset = editor?.caretModel?.offset ?: return
|
||||
@@ -38,7 +38,7 @@ class KotlinStringTemplateBackspaceHandler : BackspaceHandlerDelegate() {
|
||||
editor.document.deleteString(offset, offset + 1)
|
||||
}
|
||||
|
||||
override fun charDeleted(c: Char, file: PsiFile?, editor: Editor?): Boolean {
|
||||
override fun charDeleted(c: Char, file: PsiFile, editor: Editor): Boolean {
|
||||
return false
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* 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.editor
|
||||
|
||||
import com.intellij.codeInsight.CodeInsightSettings
|
||||
import com.intellij.codeInsight.editorActions.BackspaceHandlerDelegate
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.intellij.openapi.editor.ex.EditorEx
|
||||
import com.intellij.psi.PsiFile
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
|
||||
class KotlinStringTemplateBackspaceHandler : BackspaceHandlerDelegate() {
|
||||
override fun beforeCharDeleted(c: Char, file: PsiFile?, editor: Editor?) {
|
||||
if (c != '{' || file !is KtFile || !CodeInsightSettings.getInstance().AUTOINSERT_PAIR_BRACKET) return
|
||||
|
||||
val offset = editor?.caretModel?.offset ?: return
|
||||
|
||||
val highlighter = (editor as EditorEx).highlighter
|
||||
val iterator = highlighter.createIterator(offset)
|
||||
if (iterator.tokenType != KtTokens.LONG_TEMPLATE_ENTRY_END) return
|
||||
iterator.retreat()
|
||||
if (iterator.tokenType != KtTokens.LONG_TEMPLATE_ENTRY_START) return
|
||||
editor.document.deleteString(offset, offset + 1)
|
||||
}
|
||||
|
||||
override fun charDeleted(c: Char, file: PsiFile?, editor: Editor?): Boolean {
|
||||
return false
|
||||
}
|
||||
}
|
||||
@@ -49,20 +49,9 @@ import org.jetbrains.kotlin.util.findCallableMemberBySignature
|
||||
import org.jetbrains.kotlin.utils.DFS
|
||||
import org.jetbrains.kotlin.utils.ifEmpty
|
||||
import java.util.ArrayList
|
||||
import kotlin.collections.Collection
|
||||
import kotlin.collections.HashMap
|
||||
import kotlin.collections.LinkedHashSet
|
||||
import kotlin.collections.List
|
||||
import kotlin.collections.Set
|
||||
import kotlin.collections.emptyList
|
||||
import kotlin.collections.emptySet
|
||||
import kotlin.collections.filter
|
||||
import kotlin.collections.flatMap
|
||||
import kotlin.collections.forEach
|
||||
import kotlin.collections.getOrPut
|
||||
import kotlin.collections.listOf
|
||||
import kotlin.collections.mapNotNullTo
|
||||
import kotlin.collections.plus
|
||||
|
||||
class ChangeSuspendInHierarchyFix(
|
||||
element: KtNamedFunction,
|
||||
@@ -83,7 +72,7 @@ class ChangeSuspendInHierarchyFix(
|
||||
private fun findAllFunctionToProcess(project: Project): Set<KtNamedFunction> {
|
||||
val result = LinkedHashSet<KtNamedFunction>()
|
||||
|
||||
val progressIndicator = ProgressManager.getInstance().progressIndicator
|
||||
val progressIndicator = ProgressManager.getInstance().progressIndicator!!
|
||||
|
||||
val function = element ?: return emptySet()
|
||||
val functionDescriptor = function.unsafeResolveToDescriptor() as FunctionDescriptor
|
||||
|
||||
@@ -0,0 +1,193 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
import com.intellij.codeInsight.intention.IntentionAction
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.intellij.openapi.progress.ProgressManager
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.psi.PsiNamedElement
|
||||
import org.jetbrains.kotlin.asJava.unwrapped
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptorWithResolutionScopes
|
||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||
import org.jetbrains.kotlin.diagnostics.Diagnostic
|
||||
import org.jetbrains.kotlin.diagnostics.Errors
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.unsafeResolveToDescriptor
|
||||
import org.jetbrains.kotlin.idea.codeInsight.DescriptorToSourceUtilsIde
|
||||
import org.jetbrains.kotlin.idea.runSynchronouslyWithProgress
|
||||
import org.jetbrains.kotlin.idea.search.declarationsSearch.HierarchySearchRequest
|
||||
import org.jetbrains.kotlin.idea.search.declarationsSearch.searchInheritors
|
||||
import org.jetbrains.kotlin.idea.util.application.runReadAction
|
||||
import org.jetbrains.kotlin.idea.util.application.runWriteAction
|
||||
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
import org.jetbrains.kotlin.psi.KtClassOrObject
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.psi.KtNamedFunction
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||
import org.jetbrains.kotlin.resolve.OverridingUtil
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.isSubclassOf
|
||||
import org.jetbrains.kotlin.resolve.source.getPsi
|
||||
import org.jetbrains.kotlin.types.substitutions.getTypeSubstitutor
|
||||
import org.jetbrains.kotlin.util.findCallableMemberBySignature
|
||||
import org.jetbrains.kotlin.utils.DFS
|
||||
import org.jetbrains.kotlin.utils.ifEmpty
|
||||
import java.util.ArrayList
|
||||
import kotlin.collections.Collection
|
||||
import kotlin.collections.HashMap
|
||||
import kotlin.collections.LinkedHashSet
|
||||
import kotlin.collections.List
|
||||
import kotlin.collections.Set
|
||||
import kotlin.collections.emptyList
|
||||
import kotlin.collections.emptySet
|
||||
import kotlin.collections.filter
|
||||
import kotlin.collections.flatMap
|
||||
import kotlin.collections.forEach
|
||||
import kotlin.collections.getOrPut
|
||||
import kotlin.collections.listOf
|
||||
import kotlin.collections.mapNotNullTo
|
||||
import kotlin.collections.plus
|
||||
|
||||
class ChangeSuspendInHierarchyFix(
|
||||
element: KtNamedFunction,
|
||||
private val addModifier: Boolean
|
||||
) : KotlinQuickFixAction<KtNamedFunction>(element) {
|
||||
override fun getFamilyName(): String {
|
||||
return if (addModifier) {
|
||||
"Add 'suspend' modifier to all functions in hierarchy"
|
||||
} else {
|
||||
"Remove 'suspend' modifier from all functions in hierarchy"
|
||||
}
|
||||
}
|
||||
|
||||
override fun getText() = familyName
|
||||
|
||||
override fun startInWriteAction() = false
|
||||
|
||||
private fun findAllFunctionToProcess(project: Project): Set<KtNamedFunction> {
|
||||
val result = LinkedHashSet<KtNamedFunction>()
|
||||
|
||||
val progressIndicator = ProgressManager.getInstance().progressIndicator
|
||||
|
||||
val function = element ?: return emptySet()
|
||||
val functionDescriptor = function.unsafeResolveToDescriptor() as FunctionDescriptor
|
||||
|
||||
val baseFunctionDescriptors = functionDescriptor.findTopMostOverriddables()
|
||||
baseFunctionDescriptors.forEach { baseFunctionDescriptor ->
|
||||
val baseClassDescriptor = baseFunctionDescriptor.containingDeclaration as? ClassDescriptor ?: return@forEach
|
||||
val baseClass = DescriptorToSourceUtilsIde.getAnyDeclaration(project, baseClassDescriptor) ?: return@forEach
|
||||
|
||||
val name = (baseClass as? PsiNamedElement)?.name ?: return@forEach
|
||||
progressIndicator.text = "Looking for class $name inheritors..."
|
||||
val classes = listOf(baseClass) + HierarchySearchRequest(baseClass, baseClass.useScope).searchInheritors()
|
||||
classes.mapNotNullTo(result) {
|
||||
val subClass = it.unwrapped as? KtClassOrObject ?: return@mapNotNullTo null
|
||||
val classDescriptor = subClass.unsafeResolveToDescriptor() as ClassDescriptor
|
||||
val substitutor = getTypeSubstitutor(baseClassDescriptor.defaultType, classDescriptor.defaultType)
|
||||
?: return@mapNotNullTo null
|
||||
val signatureInSubClass = baseFunctionDescriptor.substitute(substitutor) as FunctionDescriptor
|
||||
val subFunctionDescriptor = classDescriptor.findCallableMemberBySignature(signatureInSubClass, true)
|
||||
?: return@mapNotNullTo null
|
||||
subFunctionDescriptor.source.getPsi() as? KtNamedFunction
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
override fun invoke(project: Project, editor: Editor?, file: KtFile) {
|
||||
val functions = project.runSynchronouslyWithProgress("Analyzing class hierarchy...", true) {
|
||||
runReadAction { findAllFunctionToProcess(project) }
|
||||
} ?: return
|
||||
|
||||
runWriteAction {
|
||||
functions.forEach {
|
||||
if (addModifier) {
|
||||
it.addModifier(KtTokens.SUSPEND_KEYWORD)
|
||||
}
|
||||
else {
|
||||
it.removeModifier(KtTokens.SUSPEND_KEYWORD)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object : KotlinIntentionActionsFactory() {
|
||||
fun FunctionDescriptor.findTopMostOverriddables(): List<FunctionDescriptor> {
|
||||
val overridablesCache = HashMap<FunctionDescriptor, List<FunctionDescriptor>>()
|
||||
|
||||
fun FunctionDescriptor.getOverridables(): List<FunctionDescriptor> {
|
||||
return overridablesCache.getOrPut(this) {
|
||||
val classDescriptor = containingDeclaration as? ClassDescriptorWithResolutionScopes ?: return emptyList()
|
||||
DescriptorUtils.getSuperclassDescriptors(classDescriptor).flatMap { superClassDescriptor ->
|
||||
if (superClassDescriptor !is ClassDescriptorWithResolutionScopes) return@flatMap emptyList<FunctionDescriptor>()
|
||||
val candidates = superClassDescriptor.unsubstitutedMemberScope.getContributedFunctions(name, NoLookupLocation.FROM_IDE)
|
||||
val substitutor = getTypeSubstitutor(superClassDescriptor.defaultType, classDescriptor.defaultType)
|
||||
?: return@flatMap emptyList<FunctionDescriptor>()
|
||||
candidates.filter {
|
||||
val signature = it.substitute(substitutor) as FunctionDescriptor
|
||||
classDescriptor.findCallableMemberBySignature(signature, true) == this
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return DFS.dfs(
|
||||
listOf(this),
|
||||
{ it?.getOverridables() ?: emptyList() },
|
||||
object : DFS.CollectingNodeHandler<FunctionDescriptor, FunctionDescriptor, ArrayList<FunctionDescriptor>>(ArrayList()) {
|
||||
override fun afterChildren(current: FunctionDescriptor) {
|
||||
if (current.getOverridables().isEmpty()) {
|
||||
result.add(current)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun Collection<DeclarationDescriptor>.getOverridables(
|
||||
currentDescriptor: FunctionDescriptor
|
||||
): List<DeclarationDescriptor> {
|
||||
val currentClassDescriptor = currentDescriptor.containingDeclaration as? ClassDescriptor ?: return emptyList()
|
||||
return filter {
|
||||
if (it !is FunctionDescriptor || it == currentDescriptor) return@filter false
|
||||
if (it.isSuspend == currentDescriptor.isSuspend) return@filter false
|
||||
val containingClassDescriptor = it.containingDeclaration as? ClassDescriptor ?: return@filter false
|
||||
if (!currentClassDescriptor.isSubclassOf(containingClassDescriptor)) return@filter false
|
||||
val substitutor = getTypeSubstitutor(
|
||||
containingClassDescriptor.defaultType,
|
||||
currentClassDescriptor.defaultType
|
||||
) ?: return@filter false
|
||||
val signatureInCurrentClass = it.substitute(substitutor) ?: return@filter false
|
||||
OverridingUtil.DEFAULT.isOverridableBy(signatureInCurrentClass, currentDescriptor, null).result ==
|
||||
OverridingUtil.OverrideCompatibilityInfo.Result.CONFLICT
|
||||
}
|
||||
}
|
||||
|
||||
override fun doCreateActions(diagnostic: Diagnostic): List<IntentionAction> {
|
||||
val currentFunction = diagnostic.psiElement as? KtNamedFunction ?: return emptyList()
|
||||
val currentDescriptor = currentFunction.unsafeResolveToDescriptor() as FunctionDescriptor
|
||||
Errors.CONFLICTING_OVERLOADS.cast(diagnostic).a.getOverridables(currentDescriptor).ifEmpty { return emptyList() }
|
||||
|
||||
return listOf(
|
||||
ChangeSuspendInHierarchyFix(currentFunction, true),
|
||||
ChangeSuspendInHierarchyFix(currentFunction, false)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
+31
-18
@@ -18,10 +18,14 @@ package org.jetbrains.kotlin.idea.quickfix.crossLanguage
|
||||
|
||||
import com.intellij.codeInsight.intention.IntentionAction
|
||||
import com.intellij.codeInsight.intention.QuickFixFactory
|
||||
import com.intellij.lang.jvm.*
|
||||
import com.intellij.lang.jvm.JvmClass
|
||||
import com.intellij.lang.jvm.JvmElement
|
||||
import com.intellij.lang.jvm.JvmModifier
|
||||
import com.intellij.lang.jvm.JvmModifiersOwner
|
||||
import com.intellij.lang.jvm.actions.*
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.psi.*
|
||||
import com.intellij.psi.codeStyle.SuggestedNameInfo
|
||||
import com.intellij.psi.impl.source.tree.java.PsiReferenceExpressionImpl
|
||||
import org.jetbrains.kotlin.asJava.classes.KtLightClassForFacade
|
||||
import org.jetbrains.kotlin.asJava.classes.KtLightClassForSourceDeclaration
|
||||
@@ -56,6 +60,7 @@ import org.jetbrains.kotlin.load.java.structure.impl.JavaTypeParameterImpl
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.visibilityModifierType
|
||||
import org.jetbrains.kotlin.resolve.annotations.JVM_FIELD_ANNOTATION_FQ_NAME
|
||||
import org.jetbrains.kotlin.resolve.annotations.JVM_STATIC_ANNOTATION_FQ_NAME
|
||||
import org.jetbrains.kotlin.storage.LockBasedStorageManager
|
||||
@@ -160,14 +165,14 @@ class KotlinElementActionsFactory : JvmElementActionsFactory() {
|
||||
|
||||
private inline fun <reified T : KtElement> JvmElement.toKtElement() = sourceElement?.unwrapped as? T
|
||||
|
||||
private fun fakeParametersExpressions(parameters: List<JvmParameter>, project: Project): Array<PsiExpression>? =
|
||||
private fun fakeParametersExpressions(parameters: List<Pair<SuggestedNameInfo, List<ExpectedType>>>, project: Project): Array<PsiExpression>? =
|
||||
when {
|
||||
parameters.isEmpty() -> emptyArray()
|
||||
else -> JavaPsiFacade
|
||||
.getElementFactory(project)
|
||||
.createParameterList(
|
||||
parameters.map { it.name }.toTypedArray(),
|
||||
parameters.map { it.type as? PsiType ?: return null }.toTypedArray()
|
||||
parameters.map { it.first.names.firstOrNull() }.toTypedArray(),
|
||||
parameters.map { JvmPsiConversionHelper.getInstance(project).asPsiType(it) ?: return null }.toTypedArray()
|
||||
)
|
||||
.parameters
|
||||
.map(::FakeExpressionFromParameter)
|
||||
@@ -243,10 +248,15 @@ class KotlinElementActionsFactory : JvmElementActionsFactory() {
|
||||
|
||||
val modifier = request.modifier
|
||||
val shouldPresent = request.shouldPresent
|
||||
val (kToken, shouldPresentMapped) = if (JvmModifier.FINAL == modifier)
|
||||
KtTokens.OPEN_KEYWORD to !shouldPresent
|
||||
else
|
||||
javaPsiModifiersMapping[modifier] to shouldPresent
|
||||
//TODO: make similar to `createAddMethodActions`
|
||||
val (kToken, shouldPresentMapped) = when {
|
||||
modifier == JvmModifier.FINAL -> KtTokens.OPEN_KEYWORD to !shouldPresent
|
||||
modifier == JvmModifier.PUBLIC && shouldPresent ->
|
||||
kModifierOwner.visibilityModifierType()
|
||||
?.takeIf { it != KtTokens.DEFAULT_VISIBILITY_KEYWORD }
|
||||
?.let { it to false } ?: return emptyList()
|
||||
else -> javaPsiModifiersMapping[modifier] to shouldPresent
|
||||
}
|
||||
if (kToken == null) return emptyList()
|
||||
|
||||
val action = if (shouldPresentMapped)
|
||||
@@ -256,19 +266,18 @@ class KotlinElementActionsFactory : JvmElementActionsFactory() {
|
||||
return listOfNotNull(action)
|
||||
}
|
||||
|
||||
override fun createAddConstructorActions(targetClass: JvmClass, request: MemberRequest.Constructor): List<IntentionAction> {
|
||||
override fun createAddConstructorActions(targetClass: JvmClass, request: CreateConstructorRequest): List<IntentionAction> {
|
||||
val targetKtClass = targetClass.toKtClassOrFile() as? KtClass ?: return emptyList()
|
||||
|
||||
if (request.typeParameters.isNotEmpty()) return emptyList()
|
||||
|
||||
val modifierBuilder = ModifierBuilder(targetKtClass).apply { addJvmModifiers(request.modifiers) }
|
||||
if (!modifierBuilder.isValid) return emptyList()
|
||||
|
||||
val resolutionFacade = targetKtClass.getResolutionFacade()
|
||||
val nullableAnyType = resolutionFacade.moduleDescriptor.builtIns.nullableAnyType
|
||||
val parameterInfos = request.parameters.mapIndexed { index, param ->
|
||||
val ktType = (param.type as? PsiType)?.resolveToKotlinType(resolutionFacade) ?: nullableAnyType
|
||||
val name = param.name ?: "arg${index + 1}"
|
||||
val helper = JvmPsiConversionHelper.getInstance(targetKtClass.project)
|
||||
val parameters = request.parameters as List<Pair<SuggestedNameInfo, List<ExpectedType>>>
|
||||
val parameterInfos = parameters.mapIndexed { index, param: Pair<SuggestedNameInfo, List<ExpectedType>> ->
|
||||
val ktType = helper.asPsiType(param)?.resolveToKotlinType(resolutionFacade) ?: nullableAnyType
|
||||
val name = param.first.names.firstOrNull() ?: "arg${index + 1}"
|
||||
ParameterInfo(TypeInfo(ktType, Variance.IN_VARIANCE), listOf(name))
|
||||
}
|
||||
val needPrimary = !targetKtClass.hasExplicitPrimaryConstructor()
|
||||
@@ -288,7 +297,7 @@ class KotlinElementActionsFactory : JvmElementActionsFactory() {
|
||||
val primaryConstructor = targetKtClass.primaryConstructor ?: return@run null
|
||||
val lightMethod = primaryConstructor.toLightMethods().firstOrNull() ?: return@run null
|
||||
val project = targetKtClass.project
|
||||
val fakeParametersExpressions = fakeParametersExpressions(request.parameters, project) ?: return@run null
|
||||
val fakeParametersExpressions = fakeParametersExpressions(parameters, project) ?: return@run null
|
||||
QuickFixFactory.getInstance()
|
||||
.createChangeMethodSignatureFromUsageFix(
|
||||
lightMethod,
|
||||
@@ -369,7 +378,8 @@ class KotlinElementActionsFactory : JvmElementActionsFactory() {
|
||||
|
||||
val resolutionFacade = targetContainer.getResolutionFacade()
|
||||
val returnTypeInfo = request.returnType.toKotlinTypeInfo(resolutionFacade)
|
||||
val parameterInfos = request.parameters.map { (suggestedNames, expectedTypes) ->
|
||||
val parameters = request.parameters as List<Pair<SuggestedNameInfo, List<ExpectedType>>>
|
||||
val parameterInfos = parameters.map { (suggestedNames, expectedTypes) ->
|
||||
ParameterInfo(expectedTypes.toKotlinTypeInfo(resolutionFacade), suggestedNames.names.toList())
|
||||
}
|
||||
val functionInfo = FunctionInfo(
|
||||
@@ -388,4 +398,7 @@ class KotlinElementActionsFactory : JvmElementActionsFactory() {
|
||||
}
|
||||
return listOf(action)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun JvmPsiConversionHelper.asPsiType(param: Pair<SuggestedNameInfo, List<ExpectedType>>): PsiType? =
|
||||
param.second.firstOrNull()?.theType?.let { convertType(it) }
|
||||
|
||||
+391
@@ -0,0 +1,391 @@
|
||||
/*
|
||||
* 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.crossLanguage
|
||||
|
||||
import com.intellij.codeInsight.intention.IntentionAction
|
||||
import com.intellij.codeInsight.intention.QuickFixFactory
|
||||
import com.intellij.lang.jvm.*
|
||||
import com.intellij.lang.jvm.actions.*
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.psi.*
|
||||
import com.intellij.psi.impl.source.tree.java.PsiReferenceExpressionImpl
|
||||
import org.jetbrains.kotlin.asJava.classes.KtLightClassForFacade
|
||||
import org.jetbrains.kotlin.asJava.classes.KtLightClassForSourceDeclaration
|
||||
import org.jetbrains.kotlin.asJava.toLightMethods
|
||||
import org.jetbrains.kotlin.asJava.unwrapped
|
||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||
import org.jetbrains.kotlin.descriptors.Modality
|
||||
import org.jetbrains.kotlin.descriptors.SourceElement
|
||||
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.impl.ClassDescriptorImpl
|
||||
import org.jetbrains.kotlin.descriptors.impl.MutablePackageFragmentDescriptor
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
|
||||
import org.jetbrains.kotlin.idea.core.appendModifier
|
||||
import org.jetbrains.kotlin.idea.quickfix.AddModifierFix
|
||||
import org.jetbrains.kotlin.idea.quickfix.RemoveModifierFix
|
||||
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.*
|
||||
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.createCallable.CreateCallableFromUsageFix
|
||||
import org.jetbrains.kotlin.idea.resolve.ResolutionFacade
|
||||
import org.jetbrains.kotlin.idea.util.approximateFlexibleTypes
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
import org.jetbrains.kotlin.load.java.components.TypeUsage
|
||||
import org.jetbrains.kotlin.load.java.lazy.JavaResolverComponents
|
||||
import org.jetbrains.kotlin.load.java.lazy.LazyJavaResolverContext
|
||||
import org.jetbrains.kotlin.load.java.lazy.TypeParameterResolver
|
||||
import org.jetbrains.kotlin.load.java.lazy.child
|
||||
import org.jetbrains.kotlin.load.java.lazy.descriptors.LazyJavaTypeParameterDescriptor
|
||||
import org.jetbrains.kotlin.load.java.lazy.types.JavaTypeAttributes
|
||||
import org.jetbrains.kotlin.load.java.lazy.types.JavaTypeResolver
|
||||
import org.jetbrains.kotlin.load.java.structure.JavaTypeParameter
|
||||
import org.jetbrains.kotlin.load.java.structure.impl.JavaTypeImpl
|
||||
import org.jetbrains.kotlin.load.java.structure.impl.JavaTypeParameterImpl
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.resolve.annotations.JVM_FIELD_ANNOTATION_FQ_NAME
|
||||
import org.jetbrains.kotlin.resolve.annotations.JVM_STATIC_ANNOTATION_FQ_NAME
|
||||
import org.jetbrains.kotlin.storage.LockBasedStorageManager
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.Variance
|
||||
import org.jetbrains.kotlin.types.typeUtil.supertypes
|
||||
|
||||
class KotlinElementActionsFactory : JvmElementActionsFactory() {
|
||||
companion object {
|
||||
val javaPsiModifiersMapping = mapOf(
|
||||
JvmModifier.PRIVATE to KtTokens.PRIVATE_KEYWORD,
|
||||
JvmModifier.PUBLIC to KtTokens.PUBLIC_KEYWORD,
|
||||
JvmModifier.PROTECTED to KtTokens.PUBLIC_KEYWORD,
|
||||
JvmModifier.ABSTRACT to KtTokens.ABSTRACT_KEYWORD
|
||||
)
|
||||
}
|
||||
|
||||
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 ModifierBuilder(
|
||||
private val targetContainer: KtElement,
|
||||
private val allowJvmStatic: Boolean = true
|
||||
) {
|
||||
private val psiFactory = KtPsiFactory(targetContainer.project)
|
||||
|
||||
val modifierList = psiFactory.createEmptyModifierList()
|
||||
|
||||
private fun JvmModifier.transformAndAppend(): Boolean {
|
||||
javaPsiModifiersMapping[this]?.let {
|
||||
modifierList.appendModifier(it)
|
||||
return true
|
||||
}
|
||||
|
||||
when (this) {
|
||||
JvmModifier.STATIC -> {
|
||||
if (allowJvmStatic && targetContainer is KtClassOrObject) {
|
||||
addAnnotation(JVM_STATIC_ANNOTATION_FQ_NAME)
|
||||
}
|
||||
}
|
||||
JvmModifier.ABSTRACT -> modifierList.appendModifier(KtTokens.ABSTRACT_KEYWORD)
|
||||
JvmModifier.FINAL -> modifierList.appendModifier(KtTokens.FINAL_KEYWORD)
|
||||
else -> return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
var isValid = true
|
||||
private set
|
||||
|
||||
fun addJvmModifier(modifier: JvmModifier) {
|
||||
isValid = isValid && modifier.transformAndAppend()
|
||||
}
|
||||
|
||||
fun addJvmModifiers(modifiers: Iterable<JvmModifier>) {
|
||||
modifiers.forEach { addJvmModifier(it) }
|
||||
}
|
||||
|
||||
fun addAnnotation(fqName: FqName) {
|
||||
if (!isValid) return
|
||||
modifierList.add(psiFactory.createAnnotationEntry("@${fqName.asString()}"))
|
||||
}
|
||||
}
|
||||
|
||||
class CreatePropertyFix(
|
||||
private val targetClass: JvmClass,
|
||||
contextElement: KtElement,
|
||||
propertyInfo: PropertyInfo
|
||||
) : CreateCallableFromUsageFix<KtElement>(contextElement, listOf(propertyInfo)) {
|
||||
override fun getFamilyName() = "Add property"
|
||||
override fun getText(): String {
|
||||
val info = callableInfos.first() as PropertyInfo
|
||||
return buildString {
|
||||
append("Add '")
|
||||
if (info.isLateinitPreferred) {
|
||||
append("lateinit ")
|
||||
}
|
||||
append(if (info.writable) "var" else "val")
|
||||
append("' property '${info.name}' to '${targetClass.name}'")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun JvmClass.toKtClassOrFile(): KtElement? {
|
||||
val psi = sourceElement
|
||||
return when (psi) {
|
||||
is KtClassOrObject -> psi
|
||||
is KtLightClassForSourceDeclaration -> psi.kotlinOrigin
|
||||
is KtLightClassForFacade -> psi.files.firstOrNull()
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
private inline fun <reified T : KtElement> JvmElement.toKtElement() = sourceElement?.unwrapped as? T
|
||||
|
||||
private fun fakeParametersExpressions(parameters: List<JvmParameter>, project: Project): Array<PsiExpression>? =
|
||||
when {
|
||||
parameters.isEmpty() -> emptyArray()
|
||||
else -> JavaPsiFacade
|
||||
.getElementFactory(project)
|
||||
.createParameterList(
|
||||
parameters.map { it.name }.toTypedArray(),
|
||||
parameters.map { it.type as? PsiType ?: return null }.toTypedArray()
|
||||
)
|
||||
.parameters
|
||||
.map(::FakeExpressionFromParameter)
|
||||
.toTypedArray()
|
||||
}
|
||||
|
||||
private fun PsiType.collectTypeParameters(): List<PsiTypeParameter> {
|
||||
val results = ArrayList<PsiTypeParameter>()
|
||||
accept(
|
||||
object : PsiTypeVisitor<Unit>() {
|
||||
override fun visitArrayType(arrayType: PsiArrayType) {
|
||||
arrayType.componentType.accept(this)
|
||||
}
|
||||
|
||||
override fun visitClassType(classType: PsiClassType) {
|
||||
(classType.resolve() as? PsiTypeParameter)?.let { results += it }
|
||||
classType.parameters.forEach { it.accept(this) }
|
||||
}
|
||||
|
||||
override fun visitWildcardType(wildcardType: PsiWildcardType) {
|
||||
wildcardType.bound?.accept(this)
|
||||
}
|
||||
}
|
||||
)
|
||||
return results
|
||||
}
|
||||
|
||||
private fun PsiType.resolveToKotlinType(resolutionFacade: ResolutionFacade): KotlinType? {
|
||||
val typeParameters = collectTypeParameters()
|
||||
val components = resolutionFacade.getFrontendService(JavaResolverComponents::class.java)
|
||||
val rootContext = LazyJavaResolverContext(components, TypeParameterResolver.EMPTY) { null }
|
||||
val dummyPackageDescriptor = MutablePackageFragmentDescriptor(resolutionFacade.moduleDescriptor, FqName("dummy"))
|
||||
val dummyClassDescriptor = ClassDescriptorImpl(
|
||||
dummyPackageDescriptor,
|
||||
Name.identifier("Dummy"),
|
||||
Modality.FINAL,
|
||||
ClassKind.CLASS,
|
||||
emptyList(),
|
||||
SourceElement.NO_SOURCE,
|
||||
false,
|
||||
LockBasedStorageManager.NO_LOCKS
|
||||
)
|
||||
val typeParameterResolver = object : TypeParameterResolver {
|
||||
override fun resolveTypeParameter(javaTypeParameter: JavaTypeParameter): TypeParameterDescriptor? {
|
||||
val psiTypeParameter = (javaTypeParameter as JavaTypeParameterImpl).psi
|
||||
val index = typeParameters.indexOf(psiTypeParameter)
|
||||
if (index < 0) return null
|
||||
return LazyJavaTypeParameterDescriptor(rootContext.child(this), javaTypeParameter, index, dummyClassDescriptor)
|
||||
}
|
||||
}
|
||||
val typeResolver = JavaTypeResolver(rootContext, typeParameterResolver)
|
||||
val attributes = JavaTypeAttributes(TypeUsage.COMMON)
|
||||
return typeResolver.transformJavaType(JavaTypeImpl.create(this), attributes).approximateFlexibleTypes(preferNotNull = true)
|
||||
}
|
||||
|
||||
private fun ExpectedTypes.toKotlinTypeInfo(resolutionFacade: ResolutionFacade): TypeInfo {
|
||||
val candidateTypes = flatMapTo(LinkedHashSet<KotlinType>()) {
|
||||
val ktType = (it.theType as? PsiType)?.resolveToKotlinType(resolutionFacade) ?: return@flatMapTo emptyList()
|
||||
when (it.theKind) {
|
||||
ExpectedType.Kind.EXACT, ExpectedType.Kind.SUBTYPE -> listOf(ktType)
|
||||
ExpectedType.Kind.SUPERTYPE -> listOf(ktType) + ktType.supertypes()
|
||||
}
|
||||
}
|
||||
if (candidateTypes.isEmpty()) {
|
||||
val nullableAnyType = resolutionFacade.moduleDescriptor.builtIns.nullableAnyType
|
||||
return TypeInfo(nullableAnyType, Variance.INVARIANT)
|
||||
}
|
||||
return TypeInfo.ByExplicitCandidateTypes(candidateTypes.toList())
|
||||
}
|
||||
|
||||
override fun createChangeModifierActions(target: JvmModifiersOwner, request: MemberRequest.Modifier): List<IntentionAction> {
|
||||
val kModifierOwner = target.toKtElement<KtModifierListOwner>() ?: return emptyList()
|
||||
|
||||
val modifier = request.modifier
|
||||
val shouldPresent = request.shouldPresent
|
||||
val (kToken, shouldPresentMapped) = if (JvmModifier.FINAL == modifier)
|
||||
KtTokens.OPEN_KEYWORD to !shouldPresent
|
||||
else
|
||||
javaPsiModifiersMapping[modifier] to shouldPresent
|
||||
if (kToken == null) return emptyList()
|
||||
|
||||
val action = if (shouldPresentMapped)
|
||||
AddModifierFix.createIfApplicable(kModifierOwner, kToken)
|
||||
else
|
||||
RemoveModifierFix(kModifierOwner, kToken, false)
|
||||
return listOfNotNull(action)
|
||||
}
|
||||
|
||||
override fun createAddConstructorActions(targetClass: JvmClass, request: MemberRequest.Constructor): List<IntentionAction> {
|
||||
val targetKtClass = targetClass.toKtClassOrFile() as? KtClass ?: return emptyList()
|
||||
|
||||
if (request.typeParameters.isNotEmpty()) return emptyList()
|
||||
|
||||
val modifierBuilder = ModifierBuilder(targetKtClass).apply { addJvmModifiers(request.modifiers) }
|
||||
if (!modifierBuilder.isValid) return emptyList()
|
||||
|
||||
val resolutionFacade = targetKtClass.getResolutionFacade()
|
||||
val nullableAnyType = resolutionFacade.moduleDescriptor.builtIns.nullableAnyType
|
||||
val parameterInfos = request.parameters.mapIndexed { index, param ->
|
||||
val ktType = (param.type as? PsiType)?.resolveToKotlinType(resolutionFacade) ?: nullableAnyType
|
||||
val name = param.name ?: "arg${index + 1}"
|
||||
ParameterInfo(TypeInfo(ktType, Variance.IN_VARIANCE), listOf(name))
|
||||
}
|
||||
val needPrimary = !targetKtClass.hasExplicitPrimaryConstructor()
|
||||
val constructorInfo = ConstructorInfo(
|
||||
parameterInfos,
|
||||
targetKtClass,
|
||||
isPrimary = needPrimary,
|
||||
modifierList = modifierBuilder.modifierList,
|
||||
withBody = true
|
||||
)
|
||||
val addConstructorAction = object : CreateCallableFromUsageFix<KtElement>(targetKtClass, listOf(constructorInfo)) {
|
||||
override fun getFamilyName() = "Add method"
|
||||
override fun getText() = "Add ${if (needPrimary) "primary" else "secondary"} constructor to '${targetClass.name}'"
|
||||
}
|
||||
|
||||
val changePrimaryConstructorAction = run {
|
||||
val primaryConstructor = targetKtClass.primaryConstructor ?: return@run null
|
||||
val lightMethod = primaryConstructor.toLightMethods().firstOrNull() ?: return@run null
|
||||
val project = targetKtClass.project
|
||||
val fakeParametersExpressions = fakeParametersExpressions(request.parameters, project) ?: return@run null
|
||||
QuickFixFactory.getInstance()
|
||||
.createChangeMethodSignatureFromUsageFix(
|
||||
lightMethod,
|
||||
fakeParametersExpressions,
|
||||
PsiSubstitutor.EMPTY,
|
||||
targetKtClass,
|
||||
false,
|
||||
2
|
||||
).takeIf { it.isAvailable(project, null, targetKtClass.containingFile) }
|
||||
}
|
||||
|
||||
return listOfNotNull(changePrimaryConstructorAction, addConstructorAction)
|
||||
}
|
||||
|
||||
override fun createAddPropertyActions(targetClass: JvmClass, request: MemberRequest.Property): List<IntentionAction> {
|
||||
val targetContainer = targetClass.toKtClassOrFile() ?: return emptyList()
|
||||
|
||||
val modifierBuilder = ModifierBuilder(targetContainer).apply { addJvmModifier(request.visibilityModifier) }
|
||||
if (!modifierBuilder.isValid) return emptyList()
|
||||
|
||||
val resolutionFacade = targetContainer.getResolutionFacade()
|
||||
val nullableAnyType = resolutionFacade.moduleDescriptor.builtIns.nullableAnyType
|
||||
val ktType = (request.propertyType as? PsiType)?.resolveToKotlinType(resolutionFacade) ?: nullableAnyType
|
||||
val propertyInfo = PropertyInfo(
|
||||
request.propertyName,
|
||||
TypeInfo.Empty,
|
||||
TypeInfo(ktType, Variance.INVARIANT),
|
||||
request.setterRequired,
|
||||
listOf(targetContainer),
|
||||
modifierList = modifierBuilder.modifierList,
|
||||
withInitializer = true
|
||||
)
|
||||
val propertyInfos = if (request.setterRequired) {
|
||||
listOf(propertyInfo, propertyInfo.copyProperty(isLateinitPreferred = true))
|
||||
}
|
||||
else {
|
||||
listOf(propertyInfo)
|
||||
}
|
||||
return propertyInfos.map { CreatePropertyFix(targetClass, targetContainer, it) }
|
||||
}
|
||||
|
||||
override fun createAddFieldActions(targetClass: JvmClass, request: CreateFieldRequest): List<IntentionAction> {
|
||||
val targetContainer = targetClass.toKtClassOrFile() ?: return emptyList()
|
||||
|
||||
val modifierBuilder = ModifierBuilder(targetContainer, allowJvmStatic = false).apply {
|
||||
addJvmModifiers(request.modifiers)
|
||||
addAnnotation(JVM_FIELD_ANNOTATION_FQ_NAME)
|
||||
}
|
||||
if (!modifierBuilder.isValid) return emptyList()
|
||||
|
||||
val resolutionFacade = targetContainer.getResolutionFacade()
|
||||
val typeInfo = request.fieldType.toKotlinTypeInfo(resolutionFacade)
|
||||
val writable = JvmModifier.FINAL !in request.modifiers
|
||||
val propertyInfo = PropertyInfo(
|
||||
request.fieldName,
|
||||
TypeInfo.Empty,
|
||||
typeInfo,
|
||||
writable,
|
||||
listOf(targetContainer),
|
||||
isForCompanion = JvmModifier.STATIC in request.modifiers,
|
||||
modifierList = modifierBuilder.modifierList,
|
||||
withInitializer = true
|
||||
)
|
||||
val propertyInfos = if (writable) {
|
||||
listOf(propertyInfo, propertyInfo.copyProperty(isLateinitPreferred = true))
|
||||
}
|
||||
else {
|
||||
listOf(propertyInfo)
|
||||
}
|
||||
return propertyInfos.map { CreatePropertyFix(targetClass, targetContainer, it) }
|
||||
}
|
||||
|
||||
override fun createAddMethodActions(targetClass: JvmClass, request: CreateMethodRequest): List<IntentionAction> {
|
||||
val targetContainer = targetClass.toKtClassOrFile() ?: return emptyList()
|
||||
|
||||
val modifierBuilder = ModifierBuilder(targetContainer).apply { addJvmModifiers(request.modifiers) }
|
||||
if (!modifierBuilder.isValid) return emptyList()
|
||||
|
||||
val resolutionFacade = targetContainer.getResolutionFacade()
|
||||
val returnTypeInfo = request.returnType.toKotlinTypeInfo(resolutionFacade)
|
||||
val parameterInfos = request.parameters.map { (suggestedNames, expectedTypes) ->
|
||||
ParameterInfo(expectedTypes.toKotlinTypeInfo(resolutionFacade), suggestedNames.names.toList())
|
||||
}
|
||||
val functionInfo = FunctionInfo(
|
||||
request.methodName,
|
||||
TypeInfo.Empty,
|
||||
returnTypeInfo,
|
||||
listOf(targetContainer),
|
||||
parameterInfos,
|
||||
isForCompanion = JvmModifier.STATIC in request.modifiers,
|
||||
modifierList = modifierBuilder.modifierList,
|
||||
preferEmptyBody = true
|
||||
)
|
||||
val action = object : CreateCallableFromUsageFix<KtElement>(targetContainer, listOf(functionInfo)) {
|
||||
override fun getFamilyName() = "Add method"
|
||||
override fun getText() = "Add method '${request.methodName}' to '${targetClass.name}'"
|
||||
}
|
||||
return listOf(action)
|
||||
}
|
||||
}
|
||||
+2
-3
@@ -54,9 +54,8 @@ class KotlinCallerChooser(
|
||||
previousTree: Tree?,
|
||||
callback: Consumer<Set<PsiElement>>
|
||||
): CallerChooserBase<PsiElement>(declaration, project, title, previousTree, "dummy." + KotlinFileType.EXTENSION, callback) {
|
||||
override fun createTreeNode(method: PsiElement?, called: com.intellij.util.containers.HashSet<PsiElement>, cancelCallback: Runnable): KotlinMethodNode {
|
||||
return KotlinMethodNode(method, called, myProject, cancelCallback)
|
||||
}
|
||||
override fun createTreeNodeFor(method: PsiElement?, called: HashSet<PsiElement>?, cancelCallback: Runnable?) =
|
||||
KotlinMethodNode(method, called ?: HashSet(), myProject, cancelCallback ?: Runnable {})
|
||||
|
||||
override fun findDeepestSuperMethods(method: PsiElement) =
|
||||
method.toLightMethods().singleOrNull()?.findDeepestSuperMethods()
|
||||
|
||||
+124
@@ -0,0 +1,124 @@
|
||||
/*
|
||||
* 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.changeSignature.ui
|
||||
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.psi.PsiClassOwner
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.PsiMethod
|
||||
import com.intellij.psi.PsiReference
|
||||
import com.intellij.psi.search.searches.MethodReferencesSearch
|
||||
import com.intellij.psi.search.searches.ReferencesSearch
|
||||
import com.intellij.refactoring.changeSignature.CallerChooserBase
|
||||
import com.intellij.refactoring.changeSignature.MethodNodeBase
|
||||
import com.intellij.ui.ColoredTreeCellRenderer
|
||||
import com.intellij.ui.JBColor
|
||||
import com.intellij.ui.SimpleTextAttributes
|
||||
import com.intellij.ui.treeStructure.Tree
|
||||
import com.intellij.util.Consumer
|
||||
import com.intellij.util.ui.UIUtil
|
||||
import org.jetbrains.kotlin.asJava.getRepresentativeLightMethod
|
||||
import org.jetbrains.kotlin.asJava.namedUnwrappedElement
|
||||
import org.jetbrains.kotlin.asJava.toLightMethods
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||
import org.jetbrains.kotlin.idea.KotlinFileType
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.util.getJavaMethodDescriptor
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.unsafeResolveToDescriptor
|
||||
import org.jetbrains.kotlin.idea.hierarchy.calls.CalleeReferenceProcessor
|
||||
import org.jetbrains.kotlin.idea.hierarchy.calls.KotlinCallHierarchyNodeDescriptor
|
||||
import org.jetbrains.kotlin.psi.KtClass
|
||||
import org.jetbrains.kotlin.psi.KtFunction
|
||||
import org.jetbrains.kotlin.psi.psiUtil.getElementTextWithContext
|
||||
import java.util.*
|
||||
|
||||
class KotlinCallerChooser(
|
||||
declaration: PsiElement,
|
||||
project: Project,
|
||||
title: String,
|
||||
previousTree: Tree?,
|
||||
callback: Consumer<Set<PsiElement>>
|
||||
): CallerChooserBase<PsiElement>(declaration, project, title, previousTree, "dummy." + KotlinFileType.EXTENSION, callback) {
|
||||
override fun createTreeNode(method: PsiElement?, called: com.intellij.util.containers.HashSet<PsiElement>, cancelCallback: Runnable): KotlinMethodNode {
|
||||
return KotlinMethodNode(method, called, myProject, cancelCallback)
|
||||
}
|
||||
|
||||
override fun findDeepestSuperMethods(method: PsiElement) =
|
||||
method.toLightMethods().singleOrNull()?.findDeepestSuperMethods()
|
||||
|
||||
override fun getEmptyCallerText() =
|
||||
"Caller text \nwith highlighted callee call would be shown here"
|
||||
|
||||
override fun getEmptyCalleeText() =
|
||||
"Callee text would be shown here"
|
||||
}
|
||||
|
||||
class KotlinMethodNode(
|
||||
method: PsiElement?,
|
||||
called: HashSet<PsiElement>,
|
||||
project: Project,
|
||||
cancelCallback: Runnable
|
||||
): MethodNodeBase<PsiElement>(method?.namedUnwrappedElement ?: method, called, project, cancelCallback) {
|
||||
override fun createNode(caller: PsiElement, called: HashSet<PsiElement>) =
|
||||
KotlinMethodNode(caller, called, myProject, myCancelCallback)
|
||||
|
||||
override fun customizeRendererText(renderer: ColoredTreeCellRenderer) {
|
||||
val descriptor = when (myMethod) {
|
||||
is KtFunction -> myMethod.unsafeResolveToDescriptor() as FunctionDescriptor
|
||||
is KtClass -> (myMethod.unsafeResolveToDescriptor() as ClassDescriptor).unsubstitutedPrimaryConstructor ?: return
|
||||
is PsiMethod -> myMethod.getJavaMethodDescriptor() ?: return
|
||||
else -> throw AssertionError("Invalid declaration: ${myMethod.getElementTextWithContext()}")
|
||||
}
|
||||
val containerName = generateSequence<DeclarationDescriptor>(descriptor) { it.containingDeclaration }
|
||||
.firstOrNull { it is ClassDescriptor }
|
||||
?.name
|
||||
|
||||
val renderedFunction = KotlinCallHierarchyNodeDescriptor.renderNamedFunction(descriptor)
|
||||
val renderedFunctionWithContainer =
|
||||
containerName?.let {
|
||||
"${if (it.isSpecial) "[Anonymous]" else it.asString()}.$renderedFunction"
|
||||
} ?: renderedFunction
|
||||
|
||||
val attributes = if (isEnabled)
|
||||
SimpleTextAttributes(SimpleTextAttributes.STYLE_PLAIN, UIUtil.getTreeForeground())
|
||||
else
|
||||
SimpleTextAttributes.EXCLUDED_ATTRIBUTES
|
||||
renderer.append(renderedFunctionWithContainer, attributes)
|
||||
|
||||
val packageName = (myMethod.containingFile as? PsiClassOwner)?.packageName ?: ""
|
||||
renderer.append(" ($packageName)", SimpleTextAttributes(SimpleTextAttributes.STYLE_ITALIC, JBColor.GRAY))
|
||||
}
|
||||
|
||||
override fun computeCallers(): List<PsiElement> {
|
||||
if (myMethod == null) return emptyList()
|
||||
|
||||
val callers = LinkedHashSet<PsiElement>()
|
||||
|
||||
val processor = object: CalleeReferenceProcessor(false) {
|
||||
override fun onAccept(ref: PsiReference, element: PsiElement) {
|
||||
if ((element is KtFunction || element is KtClass || element is PsiMethod) && element !in myCalled) {
|
||||
callers.add(element)
|
||||
}
|
||||
}
|
||||
}
|
||||
val query = myMethod.getRepresentativeLightMethod()?.let { MethodReferencesSearch.search(it, it.useScope, true) }
|
||||
?: ReferencesSearch.search(myMethod, myMethod.useScope)
|
||||
query.forEach { processor.process(it) }
|
||||
return callers.toList()
|
||||
}
|
||||
}
|
||||
@@ -86,7 +86,7 @@ class CopyKotlinDeclarationDialog(
|
||||
Pass { setErrorText(it, destinationComboBox) },
|
||||
packageNameField.childComponent
|
||||
)
|
||||
classNameField.text = UsageViewUtil.getShortName(declaration)
|
||||
classNameField.setText(UsageViewUtil.getShortName(declaration))
|
||||
classNameField.selectAll()
|
||||
}
|
||||
|
||||
|
||||
+184
@@ -0,0 +1,184 @@
|
||||
/*
|
||||
* 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.refactoring.copy
|
||||
|
||||
import com.intellij.ide.util.DirectoryChooser
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.roots.JavaProjectRootsUtil
|
||||
import com.intellij.openapi.ui.DialogWrapper
|
||||
import com.intellij.openapi.ui.Messages
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import com.intellij.psi.PsiDirectory
|
||||
import com.intellij.psi.PsiManager
|
||||
import com.intellij.refactoring.HelpID
|
||||
import com.intellij.refactoring.MoveDestination
|
||||
import com.intellij.refactoring.PackageWrapper
|
||||
import com.intellij.refactoring.RefactoringBundle
|
||||
import com.intellij.refactoring.copy.CopyFilesOrDirectoriesDialog
|
||||
import com.intellij.refactoring.move.moveClassesOrPackages.DestinationFolderComboBox
|
||||
import com.intellij.refactoring.ui.PackageNameReferenceEditorCombo
|
||||
import com.intellij.ui.EditorTextField
|
||||
import com.intellij.ui.RecentsManager
|
||||
import com.intellij.ui.ReferenceEditorComboWithBrowseButton
|
||||
import com.intellij.usageView.UsageViewUtil
|
||||
import com.intellij.util.IncorrectOperationException
|
||||
import com.intellij.util.ui.FormBuilder
|
||||
import com.intellij.util.ui.UIUtil
|
||||
import org.jetbrains.annotations.NonNls
|
||||
import org.jetbrains.kotlin.idea.core.getPackage
|
||||
import org.jetbrains.kotlin.idea.refactoring.Pass
|
||||
import org.jetbrains.kotlin.idea.refactoring.hasIdentifiersOnly
|
||||
import org.jetbrains.kotlin.idea.util.sourceRoot
|
||||
import org.jetbrains.kotlin.name.FqNameUnsafe
|
||||
import org.jetbrains.kotlin.psi.KtNamedDeclaration
|
||||
import java.awt.BorderLayout
|
||||
import java.awt.Font
|
||||
import javax.swing.JComponent
|
||||
import javax.swing.JLabel
|
||||
import javax.swing.JPanel
|
||||
|
||||
// Based on com.intellij.refactoring.copy.CopyClassDialog
|
||||
class CopyKotlinDeclarationDialog(
|
||||
declaration: KtNamedDeclaration,
|
||||
private val defaultTargetDirectory: PsiDirectory?,
|
||||
private val project: Project
|
||||
) : DialogWrapper(project, true) {
|
||||
private val informationLabel = JLabel()
|
||||
private val classNameField = EditorTextField("")
|
||||
private val packageLabel = JLabel()
|
||||
private lateinit var packageNameField: ReferenceEditorComboWithBrowseButton
|
||||
private val openInEditorCheckBox = CopyFilesOrDirectoriesDialog.createOpenInEditorCB()
|
||||
private val destinationComboBox = object : DestinationFolderComboBox() {
|
||||
override fun getTargetPackage() = packageNameField.text.trim()
|
||||
override fun reportBaseInTestSelectionInSource() = true
|
||||
}
|
||||
|
||||
private val originalFile = declaration.containingFile
|
||||
|
||||
var targetDirectory: MoveDestination? = null
|
||||
private set
|
||||
|
||||
val targetSourceRoot: VirtualFile?
|
||||
get() = ((destinationComboBox.comboBox.selectedItem as? DirectoryChooser.ItemWrapper)?.directory ?: originalFile).sourceRoot
|
||||
|
||||
init {
|
||||
informationLabel.text = RefactoringBundle.message("copy.class.copy.0.1", UsageViewUtil.getType(declaration), UsageViewUtil.getLongName(declaration))
|
||||
informationLabel.font = informationLabel.font.deriveFont(Font.BOLD)
|
||||
|
||||
init()
|
||||
|
||||
destinationComboBox.setData(
|
||||
project,
|
||||
defaultTargetDirectory,
|
||||
Pass { setErrorText(it, destinationComboBox) },
|
||||
packageNameField.childComponent
|
||||
)
|
||||
classNameField.text = UsageViewUtil.getShortName(declaration)
|
||||
classNameField.selectAll()
|
||||
}
|
||||
|
||||
override fun getPreferredFocusedComponent() = classNameField
|
||||
|
||||
override fun createCenterPanel() = JPanel(BorderLayout())
|
||||
|
||||
override fun createNorthPanel(): JComponent? {
|
||||
val qualifiedName = qualifiedName
|
||||
packageNameField = PackageNameReferenceEditorCombo(qualifiedName, project, RECENTS_KEY, RefactoringBundle.message("choose.destination.package"))
|
||||
packageNameField.setTextFieldPreferredWidth(Math.max(qualifiedName.length + 5, 40))
|
||||
packageLabel.text = RefactoringBundle.message("destination.package")
|
||||
packageLabel.labelFor = packageNameField
|
||||
|
||||
val label = JLabel(RefactoringBundle.message("target.destination.folder"))
|
||||
val isMultipleSourceRoots = JavaProjectRootsUtil.getSuitableDestinationSourceRoots(project).size > 1
|
||||
destinationComboBox.isVisible = isMultipleSourceRoots
|
||||
label.isVisible = isMultipleSourceRoots
|
||||
label.labelFor = destinationComboBox
|
||||
|
||||
val panel = JPanel(BorderLayout())
|
||||
panel.add(openInEditorCheckBox, BorderLayout.EAST)
|
||||
return FormBuilder.createFormBuilder()
|
||||
.addComponent(informationLabel)
|
||||
.addLabeledComponent(RefactoringBundle.message("copy.files.new.name.label"), classNameField, UIUtil.LARGE_VGAP)
|
||||
.addLabeledComponent(packageLabel, packageNameField)
|
||||
.addLabeledComponent(label, destinationComboBox)
|
||||
.addComponent(panel)
|
||||
.panel
|
||||
}
|
||||
|
||||
private val qualifiedName: String
|
||||
get() = defaultTargetDirectory?.getPackage()?.qualifiedName ?: ""
|
||||
|
||||
val newName: String?
|
||||
get() = classNameField.text
|
||||
|
||||
val openInEditor: Boolean
|
||||
get() = openInEditorCheckBox.isSelected
|
||||
|
||||
private fun checkForErrors(): String? {
|
||||
val packageName = packageNameField.text
|
||||
val newName = newName
|
||||
|
||||
val manager = PsiManager.getInstance(project)
|
||||
|
||||
if (packageName.isNotEmpty() && !FqNameUnsafe(packageName).hasIdentifiersOnly()) {
|
||||
return RefactoringBundle.message("invalid.target.package.name.specified")
|
||||
}
|
||||
|
||||
if (newName.isNullOrEmpty()) {
|
||||
return RefactoringBundle.message("no.class.name.specified")
|
||||
}
|
||||
|
||||
try {
|
||||
targetDirectory = destinationComboBox.selectDirectory(PackageWrapper(manager, packageName), false)
|
||||
}
|
||||
catch (e: IncorrectOperationException) {
|
||||
return e.message
|
||||
}
|
||||
|
||||
targetDirectory?.getTargetIfExists(defaultTargetDirectory)?.let {
|
||||
val targetFileName = newName + "." + originalFile.virtualFile.extension
|
||||
if (it.findFile(targetFileName) == originalFile) {
|
||||
return "Can't copy class to the containing file"
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
override fun doOKAction() {
|
||||
val packageName = packageNameField.text
|
||||
|
||||
val errorString = checkForErrors()
|
||||
if (errorString != null) {
|
||||
if (errorString.isNotEmpty()) {
|
||||
Messages.showMessageDialog(project, errorString, RefactoringBundle.message("error.title"), Messages.getErrorIcon())
|
||||
}
|
||||
classNameField.requestFocusInWindow()
|
||||
return
|
||||
}
|
||||
|
||||
RecentsManager.getInstance(project).registerRecentEntry(RECENTS_KEY, packageName)
|
||||
CopyFilesOrDirectoriesDialog.saveOpenInEditorState(openInEditorCheckBox.isSelected)
|
||||
|
||||
super.doOKAction()
|
||||
}
|
||||
|
||||
override fun getHelpId() = HelpID.COPY_CLASS
|
||||
|
||||
companion object {
|
||||
@NonNls private val RECENTS_KEY = "CopyKotlinDeclarationDialog.RECENTS_KEY"
|
||||
}
|
||||
}
|
||||
+3
-3
@@ -73,8 +73,8 @@ class KotlinAwareDelegatingMoveDestination(
|
||||
}
|
||||
filesToProcess.flatMap {it.declarations}.forEach { it.accept(extraElementCollector) }
|
||||
|
||||
val progressIndicator = ProgressManager.getInstance().progressIndicator
|
||||
progressIndicator?.pushState()
|
||||
val progressIndicator = ProgressManager.getInstance().progressIndicator!!
|
||||
progressIndicator.pushState()
|
||||
|
||||
val extraUsages = ArrayList<UsageInfo>()
|
||||
try {
|
||||
@@ -87,7 +87,7 @@ class KotlinAwareDelegatingMoveDestination(
|
||||
}
|
||||
}
|
||||
finally {
|
||||
progressIndicator?.popState()
|
||||
progressIndicator.popState()
|
||||
}
|
||||
|
||||
filesToProcess.forEach {
|
||||
|
||||
+97
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* 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.refactoring.move.moveClassesOrPackages
|
||||
|
||||
import com.intellij.openapi.progress.ProgressManager
|
||||
import com.intellij.psi.PsiDirectory
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.PsiPackage
|
||||
import com.intellij.psi.PsiRecursiveElementWalkingVisitor
|
||||
import com.intellij.psi.search.searches.ReferencesSearch
|
||||
import com.intellij.refactoring.MoveDestination
|
||||
import com.intellij.usageView.UsageInfo
|
||||
import com.intellij.util.containers.MultiMap
|
||||
import org.jetbrains.kotlin.idea.refactoring.move.createMoveUsageInfoIfPossible
|
||||
import org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations.KotlinDirectoryMoveTarget
|
||||
import org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations.analyzeConflictsInFile
|
||||
import org.jetbrains.kotlin.idea.search.projectScope
|
||||
import org.jetbrains.kotlin.idea.stubindex.KotlinExactPackagesIndex
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.psi.KtNamedDeclaration
|
||||
import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf
|
||||
|
||||
class KotlinAwareDelegatingMoveDestination(
|
||||
private val delegate: MoveDestination,
|
||||
private val targetPackage: PsiPackage?,
|
||||
private val targetDirectory: PsiDirectory?
|
||||
) : MoveDestination by delegate {
|
||||
override fun analyzeModuleConflicts(
|
||||
elements: MutableCollection<PsiElement>,
|
||||
conflicts: MultiMap<PsiElement, String>,
|
||||
usages: Array<out UsageInfo>
|
||||
) {
|
||||
delegate.analyzeModuleConflicts(elements, conflicts, usages)
|
||||
|
||||
if (targetPackage == null || targetDirectory == null) return
|
||||
|
||||
val project = targetDirectory.project
|
||||
val moveTarget = KotlinDirectoryMoveTarget(FqName(targetPackage.qualifiedName), targetDirectory)
|
||||
val packagesIndex = KotlinExactPackagesIndex.getInstance()
|
||||
val directoriesToMove = elements.flatMapTo(LinkedHashSet<PsiDirectory>()) {
|
||||
(it as? PsiPackage)?.directories?.toList() ?: emptyList()
|
||||
}
|
||||
val projectScope = project.projectScope()
|
||||
val filesToProcess = elements.flatMapTo(LinkedHashSet<KtFile>()) {
|
||||
if (it is PsiPackage) packagesIndex[it.qualifiedName, project, projectScope] else emptyList()
|
||||
}
|
||||
|
||||
val extraElementsForReferenceSearch = LinkedHashSet<PsiElement>()
|
||||
val extraElementCollector = object : PsiRecursiveElementWalkingVisitor() {
|
||||
override fun visitElement(element: PsiElement) {
|
||||
if (element is KtNamedDeclaration && element.hasModifier(KtTokens.INTERNAL_KEYWORD)) {
|
||||
element.parentsWithSelf.lastOrNull { it is KtNamedDeclaration }?.let { extraElementsForReferenceSearch += it }
|
||||
stopWalking()
|
||||
}
|
||||
super.visitElement(element)
|
||||
}
|
||||
}
|
||||
filesToProcess.flatMap {it.declarations}.forEach { it.accept(extraElementCollector) }
|
||||
|
||||
val progressIndicator = ProgressManager.getInstance().progressIndicator
|
||||
progressIndicator?.pushState()
|
||||
|
||||
val extraUsages = ArrayList<UsageInfo>()
|
||||
try {
|
||||
progressIndicator.text = "Looking for Usages"
|
||||
for ((index, element) in extraElementsForReferenceSearch.withIndex()) {
|
||||
progressIndicator.fraction = (index + 1)/extraElementsForReferenceSearch.size.toDouble()
|
||||
ReferencesSearch.search(element, projectScope).mapNotNullTo(extraUsages) { ref ->
|
||||
createMoveUsageInfoIfPossible(ref, element, true, false)
|
||||
}
|
||||
}
|
||||
}
|
||||
finally {
|
||||
progressIndicator?.popState()
|
||||
}
|
||||
|
||||
filesToProcess.forEach {
|
||||
analyzeConflictsInFile(it, extraUsages, moveTarget, directoriesToMove, conflicts) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -28,8 +28,8 @@ class KotlinAwareMoveClassesOrPackagesToNewDirectoryDialog(
|
||||
elementsToMove: Array<out PsiElement>,
|
||||
moveCallback: MoveCallback?
|
||||
) : MoveClassesOrPackagesToNewDirectoryDialog(directory, elementsToMove, moveCallback) {
|
||||
override fun createDestination(aPackage: PsiPackage, directory: PsiDirectory): MoveDestination {
|
||||
val delegate = super.createDestination(aPackage, directory)
|
||||
override fun createDestination(aPackage: PsiPackage, directory: PsiDirectory): MoveDestination? {
|
||||
val delegate = super.createDestination(aPackage, directory) ?: return null
|
||||
return KotlinAwareDelegatingMoveDestination(delegate, aPackage, directory)
|
||||
}
|
||||
}
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* 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.refactoring.move.moveClassesOrPackages
|
||||
|
||||
import com.intellij.psi.PsiDirectory
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.PsiPackage
|
||||
import com.intellij.refactoring.MoveDestination
|
||||
import com.intellij.refactoring.move.MoveCallback
|
||||
import com.intellij.refactoring.move.moveClassesOrPackages.MoveClassesOrPackagesToNewDirectoryDialog
|
||||
|
||||
class KotlinAwareMoveClassesOrPackagesToNewDirectoryDialog(
|
||||
directory: PsiDirectory,
|
||||
elementsToMove: Array<out PsiElement>,
|
||||
moveCallback: MoveCallback?
|
||||
) : MoveClassesOrPackagesToNewDirectoryDialog(directory, elementsToMove, moveCallback) {
|
||||
override fun createDestination(aPackage: PsiPackage, directory: PsiDirectory): MoveDestination {
|
||||
val delegate = super.createDestination(aPackage, directory)
|
||||
return KotlinAwareDelegatingMoveDestination(delegate, aPackage, directory)
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -163,7 +163,7 @@ sealed class MoveDeclarationsDelegate {
|
||||
if (lightOuterClass != null) {
|
||||
MoveInnerClassUsagesHandler.EP_NAME
|
||||
.forLanguage(usage.element!!.language)
|
||||
?.correctInnerClassUsage(usage, lightOuterClass)
|
||||
?.correctInnerClassUsage(usage, lightOuterClass, outerInstanceParameterName)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+182
@@ -0,0 +1,182 @@
|
||||
/*
|
||||
* 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.refactoring.move.moveDeclarations
|
||||
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.refactoring.move.moveInner.MoveInnerClassUsagesHandler
|
||||
import com.intellij.refactoring.util.MoveRenameUsageInfo
|
||||
import com.intellij.usageView.UsageInfo
|
||||
import com.intellij.util.containers.MultiMap
|
||||
import org.jetbrains.kotlin.asJava.toLightClass
|
||||
import org.jetbrains.kotlin.asJava.unwrapped
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.unsafeResolveToDescriptor
|
||||
import org.jetbrains.kotlin.idea.codeInsight.shorten.isToBeShortened
|
||||
import org.jetbrains.kotlin.idea.refactoring.move.*
|
||||
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.isSubclassOf
|
||||
|
||||
sealed class MoveDeclarationsDelegate {
|
||||
abstract fun getContainerChangeInfo(originalDeclaration: KtNamedDeclaration, moveTarget: KotlinMoveTarget): ContainerChangeInfo
|
||||
|
||||
open fun findInternalUsages(descriptor: MoveDeclarationsDescriptor): List<UsageInfo> = emptyList()
|
||||
|
||||
open fun collectConflicts(
|
||||
descriptor: MoveDeclarationsDescriptor,
|
||||
internalUsages: MutableSet<UsageInfo>,
|
||||
conflicts: MultiMap<PsiElement, String>
|
||||
) {
|
||||
|
||||
}
|
||||
|
||||
open fun preprocessDeclaration(descriptor: MoveDeclarationsDescriptor, originalDeclaration: KtNamedDeclaration) {
|
||||
|
||||
}
|
||||
|
||||
open fun preprocessUsages(descriptor: MoveDeclarationsDescriptor, usages: List<UsageInfo>) {
|
||||
|
||||
}
|
||||
|
||||
object TopLevel : MoveDeclarationsDelegate() {
|
||||
override fun getContainerChangeInfo(originalDeclaration: KtNamedDeclaration, moveTarget: KotlinMoveTarget): ContainerChangeInfo {
|
||||
val sourcePackage = ContainerInfo.Package(originalDeclaration.containingKtFile.packageFqName)
|
||||
val targetPackage = moveTarget.targetContainerFqName?.let { ContainerInfo.Package(it) } ?: ContainerInfo.UnknownPackage
|
||||
return ContainerChangeInfo(sourcePackage, targetPackage)
|
||||
}
|
||||
}
|
||||
|
||||
class NestedClass(
|
||||
val newClassName: String? = null,
|
||||
val outerInstanceParameterName: String? = null
|
||||
) : MoveDeclarationsDelegate() {
|
||||
override fun getContainerChangeInfo(originalDeclaration: KtNamedDeclaration, moveTarget: KotlinMoveTarget): ContainerChangeInfo {
|
||||
val originalInfo = ContainerInfo.Class(originalDeclaration.containingClassOrObject!!.fqName!!)
|
||||
val movingToClass = (moveTarget as? KotlinMoveTargetForExistingElement)?.targetElement is KtClassOrObject
|
||||
val targetContainerFqName = moveTarget.targetContainerFqName
|
||||
val newInfo = when {
|
||||
targetContainerFqName == null -> ContainerInfo.UnknownPackage
|
||||
movingToClass -> ContainerInfo.Class(targetContainerFqName)
|
||||
else -> ContainerInfo.Package(targetContainerFqName)
|
||||
}
|
||||
return ContainerChangeInfo(originalInfo, newInfo)
|
||||
}
|
||||
|
||||
override fun findInternalUsages(descriptor: MoveDeclarationsDescriptor): List<UsageInfo> {
|
||||
val classToMove = descriptor.elementsToMove.singleOrNull() as? KtClass ?: return emptyList()
|
||||
return collectOuterInstanceReferences(classToMove)
|
||||
}
|
||||
|
||||
private fun isValidTargetForImplicitCompanionAsDispatchReceiver(
|
||||
moveDescriptor: MoveDeclarationsDescriptor,
|
||||
companionDescriptor: ClassDescriptor
|
||||
): Boolean {
|
||||
val moveTarget = moveDescriptor.moveTarget
|
||||
return when (moveTarget) {
|
||||
is KotlinMoveTargetForCompanion -> true
|
||||
is KotlinMoveTargetForExistingElement -> {
|
||||
val targetClass = moveTarget.targetElement as? KtClassOrObject ?: return false
|
||||
val targetClassDescriptor = targetClass.unsafeResolveToDescriptor() as ClassDescriptor
|
||||
val companionClassDescriptor = companionDescriptor.containingDeclaration as? ClassDescriptor ?: return false
|
||||
targetClassDescriptor.isSubclassOf(companionClassDescriptor)
|
||||
}
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
|
||||
override fun collectConflicts(
|
||||
descriptor: MoveDeclarationsDescriptor,
|
||||
internalUsages: MutableSet<UsageInfo>,
|
||||
conflicts: MultiMap<PsiElement, String>
|
||||
) {
|
||||
val usageIterator = internalUsages.iterator()
|
||||
while (usageIterator.hasNext()) {
|
||||
val usage = usageIterator.next()
|
||||
val element = usage.element ?: continue
|
||||
|
||||
val isConflict = when (usage) {
|
||||
is ImplicitCompanionAsDispatchReceiverUsageInfo -> {
|
||||
if (!isValidTargetForImplicitCompanionAsDispatchReceiver(descriptor, usage.companionDescriptor)) {
|
||||
conflicts.putValue(element, "Implicit companion object will be inaccessible: ${element.text}")
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
is OuterInstanceReferenceUsageInfo -> usage.reportConflictIfAny(conflicts)
|
||||
|
||||
else -> false
|
||||
}
|
||||
if (isConflict) {
|
||||
usageIterator.remove()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun preprocessDeclaration(descriptor: MoveDeclarationsDescriptor, originalDeclaration: KtNamedDeclaration) {
|
||||
with(originalDeclaration) {
|
||||
newClassName?.let { setName(it) }
|
||||
|
||||
if (this is KtClass) {
|
||||
if ((descriptor.moveTarget as? KotlinMoveTargetForExistingElement)?.targetElement !is KtClassOrObject) {
|
||||
if (hasModifier(KtTokens.INNER_KEYWORD)) removeModifier(KtTokens.INNER_KEYWORD)
|
||||
if (hasModifier(KtTokens.PROTECTED_KEYWORD)) removeModifier(KtTokens.PROTECTED_KEYWORD)
|
||||
}
|
||||
|
||||
if (outerInstanceParameterName != null) {
|
||||
val type = (containingClassOrObject!!.unsafeResolveToDescriptor() as ClassDescriptor).defaultType
|
||||
val parameter = KtPsiFactory(project)
|
||||
.createParameter("private val $outerInstanceParameterName: ${IdeDescriptorRenderers.SOURCE_CODE.renderType(type)}")
|
||||
createPrimaryConstructorParameterListIfAbsent().addParameter(parameter).isToBeShortened = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun preprocessUsages(descriptor: MoveDeclarationsDescriptor, usages: List<UsageInfo>) {
|
||||
if (outerInstanceParameterName == null) return
|
||||
val psiFactory = KtPsiFactory(descriptor.project)
|
||||
val newOuterInstanceRef = psiFactory.createExpression(outerInstanceParameterName)
|
||||
val classToMove = descriptor.elementsToMove.singleOrNull() as? KtClass
|
||||
|
||||
for (usage in usages) {
|
||||
if (usage is MoveRenameUsageInfo) {
|
||||
val referencedNestedClass = usage.referencedElement?.unwrapped as? KtClassOrObject
|
||||
if (referencedNestedClass == classToMove) {
|
||||
val outerClass = referencedNestedClass?.containingClassOrObject
|
||||
val lightOuterClass = outerClass?.toLightClass()
|
||||
if (lightOuterClass != null) {
|
||||
MoveInnerClassUsagesHandler.EP_NAME
|
||||
.forLanguage(usage.element!!.language)
|
||||
?.correctInnerClassUsage(usage, lightOuterClass)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
when (usage) {
|
||||
is OuterInstanceReferenceUsageInfo.ExplicitThis -> {
|
||||
usage.expression?.replace(newOuterInstanceRef)
|
||||
}
|
||||
is OuterInstanceReferenceUsageInfo.ImplicitReceiver -> {
|
||||
usage.callElement?.let { it.replace(psiFactory.createExpressionByPattern("$0.$1", outerInstanceParameterName, it)) }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+6
-5
@@ -16,6 +16,7 @@
|
||||
|
||||
package org.jetbrains.kotlin.idea.refactoring.move.moveFilesOrDirectories
|
||||
|
||||
import com.intellij.openapi.progress.ProgressManager
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.psi.PsiDirectory
|
||||
import com.intellij.psi.PsiElement
|
||||
@@ -85,11 +86,11 @@ class KotlinMoveDirectoryWithClassesHelper : MoveDirectoryWithClassesHelper() {
|
||||
for ((index, usageInfo) in infos.withIndex()) {
|
||||
if (usageInfo !is FileUsagesWrapper) continue
|
||||
|
||||
project.runSynchronouslyWithProgress("Analyzing conflicts in ${usageInfo.psiFile.name}", false) {
|
||||
runReadAction {
|
||||
analyzeConflictsInFile(usageInfo.psiFile, usageInfo.usages, moveTarget, files, conflicts) {
|
||||
infos[index] = usageInfo.copy(usages = it)
|
||||
}
|
||||
ProgressManager.getInstance().progressIndicator?.text2 = "Processing ${usageInfo.psiFile.name}"
|
||||
|
||||
runReadAction {
|
||||
analyzeConflictsInFile(usageInfo.psiFile, usageInfo.usages, moveTarget, files, conflicts) {
|
||||
infos[index] = usageInfo.copy(usages = it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+150
@@ -0,0 +1,150 @@
|
||||
/*
|
||||
* 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.moveFilesOrDirectories
|
||||
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.psi.PsiDirectory
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.PsiFile
|
||||
import com.intellij.refactoring.listeners.RefactoringElementListener
|
||||
import com.intellij.refactoring.move.moveClassesOrPackages.MoveDirectoryWithClassesHelper
|
||||
import com.intellij.refactoring.move.moveFilesOrDirectories.MoveFilesOrDirectoriesUtil
|
||||
import com.intellij.usageView.UsageInfo
|
||||
import com.intellij.util.Function
|
||||
import com.intellij.util.containers.MultiMap
|
||||
import org.jetbrains.kotlin.idea.core.getPackage
|
||||
import org.jetbrains.kotlin.idea.core.quoteIfNeeded
|
||||
import org.jetbrains.kotlin.idea.refactoring.invokeOnceOnCommandFinish
|
||||
import org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations.KotlinDirectoryMoveTarget
|
||||
import org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations.MoveKotlinDeclarationsProcessor
|
||||
import org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations.analyzeConflictsInFile
|
||||
import org.jetbrains.kotlin.idea.runSynchronouslyWithProgress
|
||||
import org.jetbrains.kotlin.idea.util.application.runReadAction
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import java.util.*
|
||||
|
||||
class KotlinMoveDirectoryWithClassesHelper : MoveDirectoryWithClassesHelper() {
|
||||
private data class FileUsagesWrapper(
|
||||
val psiFile: KtFile,
|
||||
val usages: List<UsageInfo>,
|
||||
val moveDeclarationsProcessor: MoveKotlinDeclarationsProcessor?
|
||||
) : UsageInfo(psiFile)
|
||||
|
||||
private class MoveContext(
|
||||
val newParent: PsiDirectory,
|
||||
val moveDeclarationsProcessor: MoveKotlinDeclarationsProcessor?
|
||||
)
|
||||
|
||||
private val fileHandler = MoveKotlinFileHandler()
|
||||
|
||||
private var fileToMoveContext: MutableMap<PsiFile, MoveContext>? = null
|
||||
|
||||
private fun getOrCreateMoveContextMap(): MutableMap<PsiFile, MoveContext> {
|
||||
return fileToMoveContext ?: HashMap<PsiFile, MoveContext>().apply {
|
||||
fileToMoveContext = this
|
||||
invokeOnceOnCommandFinish { fileToMoveContext = null }
|
||||
}
|
||||
}
|
||||
|
||||
override fun findUsages(
|
||||
filesToMove: MutableCollection<PsiFile>,
|
||||
directoriesToMove: Array<out PsiDirectory>,
|
||||
result: MutableCollection<UsageInfo>,
|
||||
searchInComments: Boolean,
|
||||
searchInNonJavaFiles: Boolean,
|
||||
project: Project) {
|
||||
filesToMove
|
||||
.filterIsInstance<KtFile>()
|
||||
.mapTo(result) { FileUsagesWrapper(it, fileHandler.findUsages(it, null, false), null) }
|
||||
}
|
||||
|
||||
override fun preprocessUsages(
|
||||
project: Project,
|
||||
files: MutableSet<PsiFile>,
|
||||
infos: Array<UsageInfo>,
|
||||
directory: PsiDirectory?,
|
||||
conflicts: MultiMap<PsiElement, String>
|
||||
) {
|
||||
val psiPackage = directory?.getPackage() ?: return
|
||||
val moveTarget = KotlinDirectoryMoveTarget(FqName(psiPackage.qualifiedName), directory)
|
||||
for ((index, usageInfo) in infos.withIndex()) {
|
||||
if (usageInfo !is FileUsagesWrapper) continue
|
||||
|
||||
project.runSynchronouslyWithProgress("Analyzing conflicts in ${usageInfo.psiFile.name}", false) {
|
||||
runReadAction {
|
||||
analyzeConflictsInFile(usageInfo.psiFile, usageInfo.usages, moveTarget, files, conflicts) {
|
||||
infos[index] = usageInfo.copy(usages = it)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun beforeMove(psiFile: PsiFile) {
|
||||
|
||||
}
|
||||
|
||||
// Actual move logic is implemented in postProcessUsages since usages are not available here
|
||||
override fun move(
|
||||
file: PsiFile,
|
||||
moveDestination: PsiDirectory,
|
||||
oldToNewElementsMapping: MutableMap<PsiElement, PsiElement>,
|
||||
movedFiles: MutableList<PsiFile>,
|
||||
listener: RefactoringElementListener?
|
||||
): Boolean {
|
||||
if (file !is KtFile) return false
|
||||
|
||||
val moveDeclarationsProcessor = fileHandler.initMoveProcessor(file, moveDestination, false)
|
||||
val moveContextMap = getOrCreateMoveContextMap()
|
||||
moveContextMap[file] = MoveContext(moveDestination, moveDeclarationsProcessor)
|
||||
if (moveDeclarationsProcessor != null) {
|
||||
moveDestination.getPackage()?.let { newPackage ->
|
||||
file.packageDirective?.fqName = FqName(newPackage.qualifiedName).quoteIfNeeded()
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
override fun afterMove(newElement: PsiElement) {
|
||||
|
||||
}
|
||||
|
||||
override fun postProcessUsages(usages: Array<out UsageInfo>, newDirMapper: Function<PsiDirectory, PsiDirectory>) {
|
||||
val fileToMoveContext = fileToMoveContext ?: return
|
||||
try {
|
||||
val usagesToProcess = ArrayList<FileUsagesWrapper>()
|
||||
usages
|
||||
.filterIsInstance<FileUsagesWrapper>()
|
||||
.forEach body@ {
|
||||
val file = it.psiFile
|
||||
val moveContext = fileToMoveContext[file] ?: return@body
|
||||
|
||||
MoveFilesOrDirectoriesUtil.doMoveFile(file, moveContext.newParent)
|
||||
|
||||
val moveDeclarationsProcessor = moveContext.moveDeclarationsProcessor ?: return@body
|
||||
val movedFile = moveContext.newParent.findFile(file.name) ?: return@body
|
||||
|
||||
usagesToProcess += FileUsagesWrapper(movedFile as KtFile, it.usages, moveDeclarationsProcessor)
|
||||
}
|
||||
usagesToProcess.forEach { fileHandler.retargetUsages(it.usages, it.moveDeclarationsProcessor!!) }
|
||||
}
|
||||
finally {
|
||||
this.fileToMoveContext = null
|
||||
}
|
||||
}
|
||||
}
|
||||
+2
-3
@@ -91,11 +91,10 @@ class RenameKotlinClassifierProcessor : RenameKotlinPsiProcessor() {
|
||||
|
||||
override fun findCollisions(
|
||||
element: PsiElement,
|
||||
newName: String?,
|
||||
newName: String,
|
||||
allRenames: MutableMap<out PsiElement, String>,
|
||||
result: MutableList<UsageInfo>
|
||||
) {
|
||||
if (newName == null) return
|
||||
val declaration = element.namedUnwrappedElement as? KtNamedDeclaration ?: return
|
||||
val descriptor = declaration.unsafeResolveToDescriptor() as ClassifierDescriptor
|
||||
|
||||
@@ -122,7 +121,7 @@ class RenameKotlinClassifierProcessor : RenameKotlinPsiProcessor() {
|
||||
else -> null
|
||||
}
|
||||
|
||||
override fun renameElement(element: PsiElement, newName: String?, usages: Array<out UsageInfo>, listener: RefactoringElementListener?) {
|
||||
override fun renameElement(element: PsiElement, newName: String, usages: Array<out UsageInfo>, listener: RefactoringElementListener?) {
|
||||
val simpleUsages = ArrayList<UsageInfo>(usages.size)
|
||||
val ambiguousImportUsages = com.intellij.util.SmartList<UsageInfo>()
|
||||
for (usage in usages) {
|
||||
|
||||
+142
@@ -0,0 +1,142 @@
|
||||
/*
|
||||
* 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.refactoring.rename
|
||||
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.PsiReference
|
||||
import com.intellij.refactoring.JavaRefactoringSettings
|
||||
import com.intellij.refactoring.listeners.RefactoringElementListener
|
||||
import com.intellij.refactoring.rename.RenamePsiElementProcessor
|
||||
import com.intellij.usageView.UsageInfo
|
||||
import org.jetbrains.kotlin.asJava.classes.KtLightClass
|
||||
import org.jetbrains.kotlin.asJava.classes.KtLightClassForFacade
|
||||
import org.jetbrains.kotlin.asJava.classes.KtLightClassForSourceDeclaration
|
||||
import org.jetbrains.kotlin.asJava.namedUnwrappedElement
|
||||
import org.jetbrains.kotlin.descriptors.ClassifierDescriptor
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.unsafeResolveToDescriptor
|
||||
import org.jetbrains.kotlin.idea.references.KtSimpleNameReference
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
|
||||
import org.jetbrains.kotlin.utils.SmartList
|
||||
import java.util.*
|
||||
|
||||
class RenameKotlinClassifierProcessor : RenameKotlinPsiProcessor() {
|
||||
override fun canProcessElement(element: PsiElement): Boolean {
|
||||
return element is KtClassOrObject || element is KtLightClass || element is KtConstructor<*> || element is KtTypeAlias
|
||||
}
|
||||
|
||||
override fun isToSearchInComments(psiElement: PsiElement) = JavaRefactoringSettings.getInstance().RENAME_SEARCH_IN_COMMENTS_FOR_CLASS
|
||||
|
||||
override fun setToSearchInComments(element: PsiElement, enabled: Boolean) {
|
||||
JavaRefactoringSettings.getInstance().RENAME_SEARCH_IN_COMMENTS_FOR_CLASS = enabled
|
||||
}
|
||||
|
||||
override fun isToSearchForTextOccurrences(element: PsiElement) = JavaRefactoringSettings.getInstance().RENAME_SEARCH_FOR_TEXT_FOR_CLASS
|
||||
|
||||
override fun setToSearchForTextOccurrences(element: PsiElement, enabled: Boolean) {
|
||||
JavaRefactoringSettings.getInstance().RENAME_SEARCH_FOR_TEXT_FOR_CLASS = enabled
|
||||
}
|
||||
|
||||
override fun substituteElementToRename(element: PsiElement, editor: Editor?) = getClassOrObject(element)
|
||||
|
||||
override fun prepareRenaming(element: PsiElement, newName: String, allRenames: MutableMap<PsiElement, String>) {
|
||||
super.prepareRenaming(element, newName, allRenames)
|
||||
|
||||
val classOrObject = getClassOrObject(element) as? KtClassOrObject ?: return
|
||||
|
||||
val file = classOrObject.containingKtFile
|
||||
|
||||
val virtualFile = file.virtualFile
|
||||
if (virtualFile != null) {
|
||||
val nameWithoutExtensions = virtualFile.nameWithoutExtension
|
||||
if (nameWithoutExtensions == classOrObject.name) {
|
||||
val newFileName = newName + "." + virtualFile.extension
|
||||
allRenames.put(file, newFileName)
|
||||
RenamePsiElementProcessor.forElement(file).prepareRenaming(file, newFileName, allRenames)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun findReferences(element: PsiElement): Collection<PsiReference> {
|
||||
if (element is KtObjectDeclaration && element.isCompanion()) {
|
||||
return super.findReferences(element).filter { !it.isCompanionObjectClassReference() }
|
||||
}
|
||||
return super.findReferences(element)
|
||||
}
|
||||
|
||||
private fun PsiReference.isCompanionObjectClassReference(): Boolean {
|
||||
if (this !is KtSimpleNameReference) {
|
||||
return false
|
||||
}
|
||||
val bindingContext = element.analyze(BodyResolveMode.PARTIAL)
|
||||
return bindingContext[BindingContext.SHORT_REFERENCE_TO_COMPANION_OBJECT, element] != null
|
||||
}
|
||||
|
||||
override fun findCollisions(
|
||||
element: PsiElement,
|
||||
newName: String?,
|
||||
allRenames: MutableMap<out PsiElement, String>,
|
||||
result: MutableList<UsageInfo>
|
||||
) {
|
||||
if (newName == null) return
|
||||
val declaration = element.namedUnwrappedElement as? KtNamedDeclaration ?: return
|
||||
val descriptor = declaration.unsafeResolveToDescriptor() as ClassifierDescriptor
|
||||
|
||||
val collisions = SmartList<UsageInfo>()
|
||||
checkRedeclarations(descriptor, newName, collisions)
|
||||
checkOriginalUsagesRetargeting(declaration, newName, result, collisions)
|
||||
checkNewNameUsagesRetargeting(declaration, newName, collisions)
|
||||
result += collisions
|
||||
}
|
||||
|
||||
private fun getClassOrObject(element: PsiElement?): PsiElement? = when (element) {
|
||||
is KtLightClass ->
|
||||
when (element) {
|
||||
is KtLightClassForSourceDeclaration -> element.kotlinOrigin
|
||||
is KtLightClassForFacade -> element
|
||||
else -> throw AssertionError("Should not be suggested to rename element of type " + element::class.java + " " + element)
|
||||
}
|
||||
|
||||
is KtConstructor<*> ->
|
||||
element.getContainingClassOrObject()
|
||||
|
||||
is KtClassOrObject, is KtTypeAlias -> element
|
||||
|
||||
else -> null
|
||||
}
|
||||
|
||||
override fun renameElement(element: PsiElement, newName: String?, usages: Array<out UsageInfo>, listener: RefactoringElementListener?) {
|
||||
val simpleUsages = ArrayList<UsageInfo>(usages.size)
|
||||
val ambiguousImportUsages = com.intellij.util.SmartList<UsageInfo>()
|
||||
for (usage in usages) {
|
||||
if (usage.isAmbiguousImportUsage()) {
|
||||
ambiguousImportUsages += usage
|
||||
}
|
||||
else {
|
||||
simpleUsages += usage
|
||||
}
|
||||
}
|
||||
element.ambiguousImportUsages = ambiguousImportUsages
|
||||
|
||||
super.renameElement(element, newName, simpleUsages.toTypedArray(), listener)
|
||||
|
||||
usages.forEach { (it as? KtResolvableCollisionUsageInfo)?.apply() }
|
||||
}
|
||||
}
|
||||
@@ -40,7 +40,7 @@ class RenameKotlinFileProcessor : RenamePsiFileProcessor() {
|
||||
|
||||
override fun canProcessElement(element: PsiElement) = element is KtFile && ProjectRootsUtil.isInProjectSource(element)
|
||||
|
||||
override fun prepareRenaming(element: PsiElement?,
|
||||
override fun prepareRenaming(element: PsiElement,
|
||||
newName: String,
|
||||
allRenames: MutableMap<PsiElement, String>,
|
||||
scope: SearchScope) {
|
||||
|
||||
+65
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* 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.rename
|
||||
|
||||
import com.intellij.openapi.fileTypes.FileTypeManager
|
||||
import com.intellij.openapi.module.ModuleUtilCore
|
||||
import com.intellij.psi.JavaPsiFacade
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.search.GlobalSearchScope
|
||||
import com.intellij.psi.search.SearchScope
|
||||
import com.intellij.refactoring.rename.RenamePsiFileProcessor
|
||||
import org.jetbrains.kotlin.asJava.classes.KtLightClass
|
||||
import org.jetbrains.kotlin.fileClasses.JvmFileClassUtil
|
||||
import org.jetbrains.kotlin.idea.KotlinFileType
|
||||
import org.jetbrains.kotlin.idea.util.ProjectRootsUtil
|
||||
import org.jetbrains.kotlin.load.kotlin.PackagePartClassUtils
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
|
||||
class RenameKotlinFileProcessor : RenamePsiFileProcessor() {
|
||||
class FileRenamingPsiClassWrapper(
|
||||
private val psiClass: KtLightClass,
|
||||
private val file: KtFile
|
||||
) : KtLightClass by psiClass {
|
||||
override fun isValid() = file.isValid
|
||||
}
|
||||
|
||||
override fun canProcessElement(element: PsiElement) = element is KtFile && ProjectRootsUtil.isInProjectSource(element)
|
||||
|
||||
override fun prepareRenaming(element: PsiElement?,
|
||||
newName: String,
|
||||
allRenames: MutableMap<PsiElement, String>,
|
||||
scope: SearchScope) {
|
||||
val jetFile = element as? KtFile ?: return
|
||||
if (FileTypeManager.getInstance().getFileTypeByFileName(newName) != KotlinFileType.INSTANCE) {
|
||||
return
|
||||
}
|
||||
|
||||
val module = ModuleUtilCore.findModuleForPsiElement(element) ?: return
|
||||
|
||||
val fileInfo = JvmFileClassUtil.getFileClassInfoNoResolve(jetFile)
|
||||
if (!fileInfo.withJvmName) {
|
||||
val facadeFqName = fileInfo.facadeClassFqName
|
||||
val project = jetFile.project
|
||||
val facadeClass = JavaPsiFacade.getInstance(project)
|
||||
.findClass(facadeFqName.asString(), GlobalSearchScope.moduleScope(module)) as? KtLightClass
|
||||
if (facadeClass != null) {
|
||||
allRenames[FileRenamingPsiClassWrapper(facadeClass, jetFile)] = PackagePartClassUtils.getFilePartShortName(newName)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+4
-7
@@ -88,11 +88,10 @@ class RenameKotlinFunctionProcessor : RenameKotlinPsiProcessor() {
|
||||
|
||||
override fun findCollisions(
|
||||
element: PsiElement,
|
||||
newName: String?,
|
||||
newName: String,
|
||||
allRenames: Map<out PsiElement, String>,
|
||||
result: MutableList<UsageInfo>
|
||||
) {
|
||||
if (newName == null) return
|
||||
val declaration = element.unwrapped as? KtNamedFunction ?: return
|
||||
val descriptor = declaration.unsafeResolveToDescriptor()
|
||||
checkConflictsAndReplaceUsageInfos(element, allRenames, result)
|
||||
@@ -115,7 +114,7 @@ class RenameKotlinFunctionProcessor : RenameKotlinPsiProcessor() {
|
||||
|
||||
private fun substituteForExpectOrActual(element: PsiElement?) = (element?.namedUnwrappedElement as? KtNamedDeclaration)?.liftToExpected()
|
||||
|
||||
override fun substituteElementToRename(element: PsiElement?, editor: Editor?): PsiElement? {
|
||||
override fun substituteElementToRename(element: PsiElement, editor: Editor?): PsiElement? {
|
||||
substituteForExpectOrActual(element)?.let { return it }
|
||||
|
||||
val wrappedMethod = wrapPsiMethod(element) ?: return element
|
||||
@@ -179,11 +178,9 @@ class RenameKotlinFunctionProcessor : RenameKotlinPsiProcessor() {
|
||||
}
|
||||
}
|
||||
|
||||
override fun prepareRenaming(element: PsiElement, newName: String?, allRenames: MutableMap<PsiElement, String>, scope: SearchScope) {
|
||||
override fun prepareRenaming(element: PsiElement, newName: String, allRenames: MutableMap<PsiElement, String>, scope: SearchScope) {
|
||||
super.prepareRenaming(element, newName, allRenames, scope)
|
||||
|
||||
if (newName == null) return
|
||||
|
||||
if (element is KtLightMethod && getJvmName(element) == null) {
|
||||
(element.kotlinOrigin as? KtNamedFunction)?.let { allRenames[it] = newName }
|
||||
}
|
||||
@@ -213,7 +210,7 @@ class RenameKotlinFunctionProcessor : RenameKotlinPsiProcessor() {
|
||||
}
|
||||
}
|
||||
|
||||
override fun renameElement(element: PsiElement, newName: String?, usages: Array<UsageInfo>, listener: RefactoringElementListener?) {
|
||||
override fun renameElement(element: PsiElement, newName: String, usages: Array<UsageInfo>, listener: RefactoringElementListener?) {
|
||||
val simpleUsages = ArrayList<UsageInfo>(usages.size)
|
||||
val ambiguousImportUsages = SmartList<UsageInfo>()
|
||||
for (usage in usages) {
|
||||
|
||||
+248
@@ -0,0 +1,248 @@
|
||||
/*
|
||||
* 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.refactoring.rename
|
||||
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.util.Pass
|
||||
import com.intellij.psi.*
|
||||
import com.intellij.psi.search.SearchScope
|
||||
import com.intellij.refactoring.JavaRefactoringSettings
|
||||
import com.intellij.refactoring.listeners.RefactoringElementListener
|
||||
import com.intellij.refactoring.rename.RenameDialog
|
||||
import com.intellij.refactoring.rename.RenameJavaMethodProcessor
|
||||
import com.intellij.refactoring.rename.RenameProcessor
|
||||
import com.intellij.refactoring.rename.RenameUtil
|
||||
import com.intellij.refactoring.util.RefactoringUtil
|
||||
import com.intellij.usageView.UsageInfo
|
||||
import com.intellij.util.SmartList
|
||||
import org.jetbrains.kotlin.asJava.LightClassUtil
|
||||
import org.jetbrains.kotlin.asJava.elements.KtLightElement
|
||||
import org.jetbrains.kotlin.asJava.elements.KtLightMethod
|
||||
import org.jetbrains.kotlin.asJava.namedUnwrappedElement
|
||||
import org.jetbrains.kotlin.asJava.unwrapped
|
||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.unsafeResolveToDescriptor
|
||||
import org.jetbrains.kotlin.idea.refactoring.Pass
|
||||
import org.jetbrains.kotlin.idea.refactoring.checkSuperMethods
|
||||
import org.jetbrains.kotlin.idea.refactoring.checkSuperMethodsWithPopup
|
||||
import org.jetbrains.kotlin.idea.refactoring.dropOverrideKeywordIfNecessary
|
||||
import org.jetbrains.kotlin.idea.references.KtReference
|
||||
import org.jetbrains.kotlin.idea.search.declarationsSearch.findDeepestSuperMethodsKotlinAware
|
||||
import org.jetbrains.kotlin.idea.search.declarationsSearch.findDeepestSuperMethodsNoWrapping
|
||||
import org.jetbrains.kotlin.idea.search.declarationsSearch.forEachOverridingMethod
|
||||
import org.jetbrains.kotlin.idea.util.application.runReadAction
|
||||
import org.jetbrains.kotlin.idea.util.liftToExpected
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||
import java.lang.IllegalStateException
|
||||
import java.util.*
|
||||
|
||||
class RenameKotlinFunctionProcessor : RenameKotlinPsiProcessor() {
|
||||
private val javaMethodProcessorInstance = RenameJavaMethodProcessor()
|
||||
|
||||
override fun canProcessElement(element: PsiElement): Boolean {
|
||||
return element is KtNamedFunction || (element is KtLightMethod && element.kotlinOrigin is KtNamedFunction) || element is FunctionWithSupersWrapper
|
||||
}
|
||||
|
||||
override fun isToSearchInComments(psiElement: PsiElement) = JavaRefactoringSettings.getInstance().RENAME_SEARCH_IN_COMMENTS_FOR_METHOD
|
||||
|
||||
override fun setToSearchInComments(element: PsiElement, enabled: Boolean) {
|
||||
JavaRefactoringSettings.getInstance().RENAME_SEARCH_IN_COMMENTS_FOR_METHOD = enabled
|
||||
}
|
||||
|
||||
override fun isToSearchForTextOccurrences(element: PsiElement) = JavaRefactoringSettings.getInstance().RENAME_SEARCH_FOR_TEXT_FOR_METHOD
|
||||
|
||||
override fun setToSearchForTextOccurrences(element: PsiElement, enabled: Boolean) {
|
||||
JavaRefactoringSettings.getInstance().RENAME_SEARCH_FOR_TEXT_FOR_METHOD = enabled
|
||||
}
|
||||
|
||||
private fun getJvmName(element: PsiElement): String? {
|
||||
val descriptor = (element.unwrapped as? KtFunction)?.unsafeResolveToDescriptor() as? FunctionDescriptor ?: return null
|
||||
return DescriptorUtils.getJvmName(descriptor)
|
||||
}
|
||||
|
||||
override fun findReferences(element: PsiElement): Collection<PsiReference> {
|
||||
val allReferences = super.findReferences(element)
|
||||
return when {
|
||||
getJvmName(element) == null -> allReferences
|
||||
element is KtElement -> allReferences.filter { it is KtReference }
|
||||
element is KtLightElement<*, *> -> allReferences.filterNot { it is KtReference }
|
||||
else -> emptyList()
|
||||
}
|
||||
}
|
||||
|
||||
override fun findCollisions(
|
||||
element: PsiElement,
|
||||
newName: String?,
|
||||
allRenames: Map<out PsiElement, String>,
|
||||
result: MutableList<UsageInfo>
|
||||
) {
|
||||
if (newName == null) return
|
||||
val declaration = element.unwrapped as? KtNamedFunction ?: return
|
||||
val descriptor = declaration.unsafeResolveToDescriptor()
|
||||
checkConflictsAndReplaceUsageInfos(element, allRenames, result)
|
||||
result += SmartList<UsageInfo>().also { collisions ->
|
||||
checkRedeclarations(descriptor, newName, collisions)
|
||||
checkOriginalUsagesRetargeting(declaration, newName, result, collisions)
|
||||
checkNewNameUsagesRetargeting(declaration, newName, collisions)
|
||||
}
|
||||
}
|
||||
|
||||
class FunctionWithSupersWrapper(
|
||||
val originalDeclaration: KtNamedFunction,
|
||||
val supers: List<PsiElement>
|
||||
) : KtLightElement<KtNamedFunction, KtNamedFunction>, PsiNamedElement by originalDeclaration {
|
||||
override val kotlinOrigin: KtNamedFunction?
|
||||
get() = originalDeclaration
|
||||
override val clsDelegate: KtNamedFunction
|
||||
get() = originalDeclaration
|
||||
}
|
||||
|
||||
private fun substituteForExpectOrActual(element: PsiElement?) = (element?.namedUnwrappedElement as? KtNamedDeclaration)?.liftToExpected()
|
||||
|
||||
override fun substituteElementToRename(element: PsiElement?, editor: Editor?): PsiElement? {
|
||||
substituteForExpectOrActual(element)?.let { return it }
|
||||
|
||||
val wrappedMethod = wrapPsiMethod(element) ?: return element
|
||||
|
||||
val deepestSuperMethods = findDeepestSuperMethodsKotlinAware(wrappedMethod)
|
||||
val substitutedJavaElement = when {
|
||||
deepestSuperMethods.isEmpty() -> return element
|
||||
wrappedMethod.isConstructor || deepestSuperMethods.size == 1 || element !is KtNamedFunction -> {
|
||||
javaMethodProcessorInstance.substituteElementToRename(wrappedMethod, editor)
|
||||
}
|
||||
else -> {
|
||||
val chosenElements = checkSuperMethods(element, null, "rename")
|
||||
if (chosenElements.size > 1) FunctionWithSupersWrapper(element, chosenElements) else wrappedMethod
|
||||
}
|
||||
}
|
||||
|
||||
if (substitutedJavaElement is KtLightMethod && element is KtDeclaration) {
|
||||
return substitutedJavaElement.kotlinOrigin as? KtNamedFunction
|
||||
}
|
||||
|
||||
return substitutedJavaElement
|
||||
}
|
||||
|
||||
override fun substituteElementToRename(element: PsiElement, editor: Editor, renameCallback: Pass<PsiElement>) {
|
||||
fun preprocessAndPass(substitutedJavaElement: PsiElement) {
|
||||
val elementToProcess = if (substitutedJavaElement is KtLightMethod && element is KtDeclaration) {
|
||||
substitutedJavaElement.kotlinOrigin as? KtNamedFunction
|
||||
}
|
||||
else {
|
||||
substitutedJavaElement
|
||||
}
|
||||
renameCallback.pass(elementToProcess)
|
||||
}
|
||||
|
||||
substituteForExpectOrActual(element)?.let { return preprocessAndPass(it) }
|
||||
|
||||
val wrappedMethod = wrapPsiMethod(element)
|
||||
val deepestSuperMethods = if (wrappedMethod != null) {
|
||||
findDeepestSuperMethodsKotlinAware(wrappedMethod)
|
||||
} else {
|
||||
findDeepestSuperMethodsNoWrapping(element)
|
||||
}
|
||||
when {
|
||||
deepestSuperMethods.isEmpty() -> preprocessAndPass(element)
|
||||
wrappedMethod != null && (wrappedMethod.isConstructor || element !is KtNamedFunction) -> {
|
||||
javaMethodProcessorInstance.substituteElementToRename(wrappedMethod, editor, Pass(::preprocessAndPass))
|
||||
}
|
||||
else -> {
|
||||
val declaration = element.unwrapped as? KtNamedFunction ?: return
|
||||
checkSuperMethodsWithPopup(declaration, deepestSuperMethods.toList(), "Rename", editor) {
|
||||
preprocessAndPass(if (it.size > 1) FunctionWithSupersWrapper(declaration, it) else wrappedMethod ?: element)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun createRenameDialog(project: Project, element: PsiElement, nameSuggestionContext: PsiElement?, editor: Editor?): RenameDialog {
|
||||
val elementForDialog = (element as? FunctionWithSupersWrapper)?.originalDeclaration ?: element
|
||||
return object : RenameDialog(project, elementForDialog, nameSuggestionContext, editor) {
|
||||
override fun createRenameProcessor(newName: String) = RenameProcessor(getProject(), element, newName, isSearchInComments, isSearchInNonJavaFiles)
|
||||
}
|
||||
}
|
||||
|
||||
override fun prepareRenaming(element: PsiElement, newName: String?, allRenames: MutableMap<PsiElement, String>, scope: SearchScope) {
|
||||
super.prepareRenaming(element, newName, allRenames, scope)
|
||||
|
||||
if (newName == null) return
|
||||
|
||||
if (element is KtLightMethod && getJvmName(element) == null) {
|
||||
(element.kotlinOrigin as? KtNamedFunction)?.let { allRenames[it] = newName }
|
||||
}
|
||||
if (element is FunctionWithSupersWrapper) {
|
||||
allRenames.remove(element)
|
||||
}
|
||||
for (declaration in ((element as? FunctionWithSupersWrapper)?.supers ?: listOf(element))) {
|
||||
val psiMethod = wrapPsiMethod(declaration) ?: continue
|
||||
allRenames[declaration] = newName
|
||||
if (psiMethod.containingClass != null) {
|
||||
psiMethod.forEachOverridingMethod { it ->
|
||||
val overrider = (it as? PsiMirrorElement)?.prototype as? PsiMethod ?: it
|
||||
|
||||
if (overrider is SyntheticElement) return@forEachOverridingMethod true
|
||||
|
||||
val overriderName = overrider.name
|
||||
val baseName = psiMethod.name
|
||||
val newOverriderName = RefactoringUtil.suggestNewOverriderName(overriderName, baseName, newName)
|
||||
if (newOverriderName != null) {
|
||||
RenameProcessor.assertNonCompileElement(overrider)
|
||||
allRenames.put(overrider, newOverriderName)
|
||||
}
|
||||
return@forEachOverridingMethod true
|
||||
}
|
||||
javaMethodProcessorInstance.prepareRenaming(psiMethod, newName, allRenames, scope)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun renameElement(element: PsiElement, newName: String?, usages: Array<UsageInfo>, listener: RefactoringElementListener?) {
|
||||
val simpleUsages = ArrayList<UsageInfo>(usages.size)
|
||||
val ambiguousImportUsages = SmartList<UsageInfo>()
|
||||
for (usage in usages) {
|
||||
if (usage is LostDefaultValuesInOverridingFunctionUsageInfo) {
|
||||
usage.apply()
|
||||
continue
|
||||
}
|
||||
|
||||
if (usage.isAmbiguousImportUsage()) {
|
||||
ambiguousImportUsages += usage
|
||||
}
|
||||
else {
|
||||
simpleUsages += usage
|
||||
}
|
||||
}
|
||||
element.ambiguousImportUsages = ambiguousImportUsages
|
||||
|
||||
RenameUtil.doRenameGenericNamedElement(element, newName, simpleUsages.toTypedArray(), listener)
|
||||
|
||||
usages.forEach { (it as? KtResolvableCollisionUsageInfo)?.apply() }
|
||||
|
||||
(element.unwrapped as? KtNamedDeclaration)?.let(::dropOverrideKeywordIfNecessary)
|
||||
}
|
||||
|
||||
private fun wrapPsiMethod(element: PsiElement?): PsiMethod? = when (element) {
|
||||
is PsiMethod -> element
|
||||
is KtNamedFunction, is KtSecondaryConstructor -> runReadAction {
|
||||
LightClassUtil.getLightClassMethod(element as KtFunction)
|
||||
}
|
||||
else -> throw IllegalStateException("Can't be for element $element there because of canProcessElement()")
|
||||
}
|
||||
}
|
||||
+2
-3
@@ -39,11 +39,10 @@ class RenameKotlinParameterProcessor : RenameKotlinPsiProcessor() {
|
||||
|
||||
override fun findCollisions(
|
||||
element: PsiElement,
|
||||
newName: String?,
|
||||
newName: String,
|
||||
allRenames: MutableMap<out PsiElement, String>,
|
||||
result: MutableList<UsageInfo>
|
||||
) {
|
||||
if (newName == null) return
|
||||
val declaration = element.namedUnwrappedElement as? KtNamedDeclaration ?: return
|
||||
val descriptor = declaration.unsafeResolveToDescriptor() as VariableDescriptor
|
||||
|
||||
@@ -54,7 +53,7 @@ class RenameKotlinParameterProcessor : RenameKotlinPsiProcessor() {
|
||||
result += collisions
|
||||
}
|
||||
|
||||
override fun renameElement(element: PsiElement, newName: String?, usages: Array<out UsageInfo>, listener: RefactoringElementListener?) {
|
||||
override fun renameElement(element: PsiElement, newName: String, usages: Array<out UsageInfo>, listener: RefactoringElementListener?) {
|
||||
super.renameElement(element, newName, usages, listener)
|
||||
|
||||
usages.forEach { (it as? KtResolvableCollisionUsageInfo)?.apply() }
|
||||
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* 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.rename
|
||||
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.refactoring.JavaRefactoringSettings
|
||||
import com.intellij.refactoring.listeners.RefactoringElementListener
|
||||
import com.intellij.usageView.UsageInfo
|
||||
import org.jetbrains.kotlin.asJava.namedUnwrappedElement
|
||||
import org.jetbrains.kotlin.descriptors.VariableDescriptor
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.unsafeResolveToDescriptor
|
||||
import org.jetbrains.kotlin.psi.KtFunction
|
||||
import org.jetbrains.kotlin.psi.KtNamedDeclaration
|
||||
import org.jetbrains.kotlin.psi.KtParameter
|
||||
import org.jetbrains.kotlin.utils.SmartList
|
||||
|
||||
class RenameKotlinParameterProcessor : RenameKotlinPsiProcessor() {
|
||||
override fun canProcessElement(element: PsiElement) = element is KtParameter && element.ownerFunction is KtFunction
|
||||
|
||||
override fun isToSearchInComments(psiElement: PsiElement) = JavaRefactoringSettings.getInstance().RENAME_SEARCH_IN_COMMENTS_FOR_VARIABLE
|
||||
|
||||
override fun setToSearchInComments(element: PsiElement, enabled: Boolean) {
|
||||
JavaRefactoringSettings.getInstance().RENAME_SEARCH_IN_COMMENTS_FOR_VARIABLE = enabled
|
||||
}
|
||||
|
||||
override fun findCollisions(
|
||||
element: PsiElement,
|
||||
newName: String?,
|
||||
allRenames: MutableMap<out PsiElement, String>,
|
||||
result: MutableList<UsageInfo>
|
||||
) {
|
||||
if (newName == null) return
|
||||
val declaration = element.namedUnwrappedElement as? KtNamedDeclaration ?: return
|
||||
val descriptor = declaration.unsafeResolveToDescriptor() as VariableDescriptor
|
||||
|
||||
val collisions = SmartList<UsageInfo>()
|
||||
checkRedeclarations(descriptor, newName, collisions)
|
||||
checkOriginalUsagesRetargeting(declaration, newName, result, collisions)
|
||||
checkNewNameUsagesRetargeting(declaration, newName, collisions)
|
||||
result += collisions
|
||||
}
|
||||
|
||||
override fun renameElement(element: PsiElement, newName: String?, usages: Array<out UsageInfo>, listener: RefactoringElementListener?) {
|
||||
super.renameElement(element, newName, usages, listener)
|
||||
|
||||
usages.forEach { (it as? KtResolvableCollisionUsageInfo)?.apply() }
|
||||
}
|
||||
}
|
||||
+5
-6
@@ -196,11 +196,10 @@ class RenameKotlinPropertyProcessor : RenameKotlinPsiProcessor() {
|
||||
|
||||
override fun findCollisions(
|
||||
element: PsiElement,
|
||||
newName: String?,
|
||||
newName: String,
|
||||
allRenames: MutableMap<out PsiElement, String>,
|
||||
result: MutableList<UsageInfo>
|
||||
) {
|
||||
if (newName == null) return
|
||||
val declaration = element.namedUnwrappedElement as? KtNamedDeclaration ?: return
|
||||
val descriptor = declaration.unsafeResolveToDescriptor() as VariableDescriptor
|
||||
|
||||
@@ -237,8 +236,8 @@ class RenameKotlinPropertyProcessor : RenameKotlinPsiProcessor() {
|
||||
}
|
||||
}
|
||||
|
||||
override fun substituteElementToRename(element: PsiElement?, editor: Editor?): PsiElement? {
|
||||
val namedUnwrappedElement = element?.namedUnwrappedElement ?: return null
|
||||
override fun substituteElementToRename(element: PsiElement, editor: Editor?): PsiElement? {
|
||||
val namedUnwrappedElement = element.namedUnwrappedElement ?: return null
|
||||
|
||||
val callableDeclaration = namedUnwrappedElement as? KtCallableDeclaration
|
||||
?: throw IllegalStateException("Can't be for element $element there because of canProcessElement()")
|
||||
@@ -293,7 +292,7 @@ class RenameKotlinPropertyProcessor : RenameKotlinPsiProcessor() {
|
||||
override fun copy() = this
|
||||
}
|
||||
|
||||
override fun prepareRenaming(element: PsiElement, newName: String?, allRenames: MutableMap<PsiElement, String>, scope: SearchScope) {
|
||||
override fun prepareRenaming(element: PsiElement, newName: String, allRenames: MutableMap<PsiElement, String>, scope: SearchScope) {
|
||||
super.prepareRenaming(element, newName, allRenames, scope)
|
||||
|
||||
val namedUnwrappedElement = element.namedUnwrappedElement
|
||||
@@ -303,7 +302,7 @@ class RenameKotlinPropertyProcessor : RenameKotlinPsiProcessor() {
|
||||
else -> throw IllegalStateException("Can't be for element $element there because of canProcessElement()")
|
||||
}
|
||||
|
||||
val newPropertyName = if (newName != null && element is KtLightMethod) propertyNameByAccessor(newName, element) else newName
|
||||
val newPropertyName = if (element is KtLightMethod) propertyNameByAccessor(newName, element) else newName
|
||||
|
||||
val (getterJvmName, setterJvmName) = getJvmNames(namedUnwrappedElement)
|
||||
|
||||
|
||||
+479
@@ -0,0 +1,479 @@
|
||||
/*
|
||||
* 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.refactoring.rename
|
||||
|
||||
import com.intellij.navigation.NavigationItem
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.intellij.openapi.ui.Messages
|
||||
import com.intellij.openapi.util.Pass
|
||||
import com.intellij.psi.*
|
||||
import com.intellij.psi.search.SearchScope
|
||||
import com.intellij.psi.search.searches.DirectClassInheritorsSearch
|
||||
import com.intellij.psi.search.searches.OverridingMethodsSearch
|
||||
import com.intellij.refactoring.JavaRefactoringSettings
|
||||
import com.intellij.refactoring.listeners.RefactoringElementListener
|
||||
import com.intellij.refactoring.rename.RenameProcessor
|
||||
import com.intellij.refactoring.util.MoveRenameUsageInfo
|
||||
import com.intellij.refactoring.util.RefactoringUtil
|
||||
import com.intellij.usageView.UsageInfo
|
||||
import com.intellij.usageView.UsageViewUtil
|
||||
import org.jetbrains.kotlin.asJava.*
|
||||
import org.jetbrains.kotlin.asJava.classes.KtLightClass
|
||||
import org.jetbrains.kotlin.asJava.elements.KtLightDeclaration
|
||||
import org.jetbrains.kotlin.asJava.elements.KtLightMethod
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.VariableDescriptor
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.unsafeResolveToDescriptor
|
||||
import org.jetbrains.kotlin.idea.core.getDeepestSuperDeclarations
|
||||
import org.jetbrains.kotlin.idea.core.isEnumCompanionPropertyWithEntryConflict
|
||||
import org.jetbrains.kotlin.idea.core.unquote
|
||||
import org.jetbrains.kotlin.idea.refactoring.checkSuperMethodsWithPopup
|
||||
import org.jetbrains.kotlin.idea.refactoring.dropOverrideKeywordIfNecessary
|
||||
import org.jetbrains.kotlin.idea.references.KtDestructuringDeclarationReference
|
||||
import org.jetbrains.kotlin.idea.references.KtReference
|
||||
import org.jetbrains.kotlin.idea.references.KtSimpleNameReference
|
||||
import org.jetbrains.kotlin.idea.references.mainReference
|
||||
import org.jetbrains.kotlin.idea.util.application.runReadAction
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
import org.jetbrains.kotlin.load.java.JvmAbi
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject
|
||||
import org.jetbrains.kotlin.psi.psiUtil.findPropertyByName
|
||||
import org.jetbrains.kotlin.psi.psiUtil.quoteIfNeeded
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.DataClassDescriptorResolver
|
||||
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||
import org.jetbrains.kotlin.resolve.source.getPsi
|
||||
import org.jetbrains.kotlin.util.findCallableMemberBySignature
|
||||
import org.jetbrains.kotlin.utils.DFS
|
||||
import org.jetbrains.kotlin.utils.SmartList
|
||||
|
||||
class RenameKotlinPropertyProcessor : RenameKotlinPsiProcessor() {
|
||||
override fun canProcessElement(element: PsiElement): Boolean {
|
||||
val namedUnwrappedElement = element.namedUnwrappedElement
|
||||
return namedUnwrappedElement is KtProperty || (namedUnwrappedElement is KtParameter && namedUnwrappedElement.hasValOrVar())
|
||||
}
|
||||
|
||||
override fun isToSearchInComments(psiElement: PsiElement) = JavaRefactoringSettings.getInstance().RENAME_SEARCH_IN_COMMENTS_FOR_FIELD
|
||||
|
||||
override fun setToSearchInComments(element: PsiElement, enabled: Boolean) {
|
||||
JavaRefactoringSettings.getInstance().RENAME_SEARCH_IN_COMMENTS_FOR_FIELD = enabled
|
||||
}
|
||||
|
||||
override fun isToSearchForTextOccurrences(element: PsiElement) = JavaRefactoringSettings.getInstance().RENAME_SEARCH_FOR_TEXT_FOR_FIELD
|
||||
|
||||
override fun setToSearchForTextOccurrences(element: PsiElement, enabled: Boolean) {
|
||||
JavaRefactoringSettings.getInstance().RENAME_SEARCH_FOR_TEXT_FOR_FIELD = enabled
|
||||
}
|
||||
|
||||
private fun getJvmNames(element: PsiElement): Pair<String?, String?> {
|
||||
val descriptor = (element.unwrapped as? KtDeclaration)?.unsafeResolveToDescriptor() as? PropertyDescriptor ?: return null to null
|
||||
val getterName = descriptor.getter?.let { DescriptorUtils.getJvmName(it) }
|
||||
val setterName = descriptor.setter?.let { DescriptorUtils.getJvmName(it) }
|
||||
return getterName to setterName
|
||||
}
|
||||
|
||||
override fun findReferences(element: PsiElement): Collection<PsiReference> {
|
||||
val allReferences = super.findReferences(element).filterNot { it is KtDestructuringDeclarationReference }
|
||||
val (getterJvmName, setterJvmName) = getJvmNames(element)
|
||||
return when {
|
||||
getterJvmName == null && setterJvmName == null -> allReferences
|
||||
element is KtElement -> allReferences.filter {
|
||||
it is KtReference
|
||||
|| (getterJvmName == null && (it.resolve() as? PsiNamedElement)?.name != setterJvmName)
|
||||
|| (setterJvmName == null && (it.resolve() as? PsiNamedElement)?.name != getterJvmName)
|
||||
}
|
||||
element is KtLightDeclaration<*, *> -> {
|
||||
val name = element.name
|
||||
if (name == getterJvmName || name == setterJvmName) allReferences.filterNot { it is KtReference } else allReferences
|
||||
}
|
||||
else -> emptyList()
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkAccidentalOverrides(
|
||||
declaration: KtNamedDeclaration,
|
||||
newName: String,
|
||||
descriptor: VariableDescriptor,
|
||||
result: MutableList<UsageInfo>
|
||||
) {
|
||||
fun reportAccidentalOverride(candidate: PsiNamedElement) {
|
||||
val what = UsageViewUtil.getType(declaration).capitalize()
|
||||
val withWhat = candidate.renderDescription()
|
||||
val where = candidate.representativeContainer()?.renderDescription() ?: return
|
||||
val message = "$what after rename will clash with existing $withWhat in $where"
|
||||
result += BasicUnresolvableCollisionUsageInfo(candidate, candidate, message)
|
||||
}
|
||||
|
||||
if (descriptor !is PropertyDescriptor) return
|
||||
val initialClass = declaration.containingClassOrObject ?: return
|
||||
val initialClassDescriptor = descriptor.containingDeclaration as? ClassDescriptor ?: return
|
||||
|
||||
val prototype = object : PropertyDescriptor by descriptor {
|
||||
override fun getName() = Name.guessByFirstCharacter(newName)
|
||||
}
|
||||
|
||||
DFS.dfs(
|
||||
listOf(initialClassDescriptor),
|
||||
DFS.Neighbors<ClassDescriptor> { DescriptorUtils.getSuperclassDescriptors(it) },
|
||||
object : DFS.AbstractNodeHandler<ClassDescriptor, Unit>() {
|
||||
override fun beforeChildren(current: ClassDescriptor): Boolean {
|
||||
if (current == initialClassDescriptor) return true
|
||||
(current.findCallableMemberBySignature(prototype))?.let { candidateDescriptor ->
|
||||
val candidate = candidateDescriptor.source.getPsi() as? PsiNamedElement ?: return false
|
||||
reportAccidentalOverride(candidate)
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
override fun result() {
|
||||
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
if (!declaration.hasModifier(KtTokens.PRIVATE_KEYWORD)) {
|
||||
val initialPsiClass = initialClass.toLightClass() ?: return
|
||||
val prototypes = declaration.toLightMethods().mapNotNull {
|
||||
it as KtLightMethod
|
||||
val methodName = accessorNameByPropertyName(newName, it) ?: return@mapNotNull null
|
||||
object : KtLightMethod by it {
|
||||
override fun getName() = methodName
|
||||
}
|
||||
}
|
||||
DFS.dfs(
|
||||
listOf(initialPsiClass),
|
||||
DFS.Neighbors<PsiClass> { DirectClassInheritorsSearch.search(it) },
|
||||
object : DFS.AbstractNodeHandler<PsiClass, Unit>() {
|
||||
override fun beforeChildren(current: PsiClass): Boolean {
|
||||
if (current == initialPsiClass) return true
|
||||
|
||||
if (current is KtLightClass) {
|
||||
val property = current.kotlinOrigin?.findPropertyByName(newName) ?: return true
|
||||
reportAccidentalOverride(property)
|
||||
return false
|
||||
}
|
||||
|
||||
for (psiMethod in prototypes) {
|
||||
current.findMethodBySignature(psiMethod, false)?.let {
|
||||
val candidate = it.unwrapped as? PsiNamedElement ?: return true
|
||||
reportAccidentalOverride(candidate)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun result() {
|
||||
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun findCollisions(
|
||||
element: PsiElement,
|
||||
newName: String?,
|
||||
allRenames: MutableMap<out PsiElement, String>,
|
||||
result: MutableList<UsageInfo>
|
||||
) {
|
||||
if (newName == null) return
|
||||
val declaration = element.namedUnwrappedElement as? KtNamedDeclaration ?: return
|
||||
val descriptor = declaration.unsafeResolveToDescriptor() as VariableDescriptor
|
||||
|
||||
val collisions = SmartList<UsageInfo>()
|
||||
checkRedeclarations(descriptor, newName, collisions)
|
||||
checkAccidentalOverrides(declaration, newName, descriptor, collisions)
|
||||
checkOriginalUsagesRetargeting(declaration, newName, result, collisions)
|
||||
checkNewNameUsagesRetargeting(declaration, newName, collisions)
|
||||
result += collisions
|
||||
}
|
||||
|
||||
private fun chooseCallableToRename(callableDeclaration: KtCallableDeclaration): KtCallableDeclaration? {
|
||||
val deepestSuperDeclaration = findDeepestOverriddenDeclaration(callableDeclaration)
|
||||
if (deepestSuperDeclaration == null || deepestSuperDeclaration == callableDeclaration) {
|
||||
return callableDeclaration
|
||||
}
|
||||
|
||||
if (ApplicationManager.getApplication()!!.isUnitTestMode) return deepestSuperDeclaration
|
||||
|
||||
val containsText: String? =
|
||||
deepestSuperDeclaration.fqName?.parent()?.asString() ?:
|
||||
(deepestSuperDeclaration.parent as? KtClassOrObject)?.name
|
||||
|
||||
val result = Messages.showYesNoCancelDialog(
|
||||
deepestSuperDeclaration.project,
|
||||
if (containsText != null) "Do you want to rename base property from \n$containsText" else "Do you want to rename base property",
|
||||
"Rename warning",
|
||||
Messages.getQuestionIcon())
|
||||
|
||||
return when (result) {
|
||||
Messages.YES -> deepestSuperDeclaration
|
||||
Messages.NO -> callableDeclaration
|
||||
else -> /* Cancel rename */ null
|
||||
}
|
||||
}
|
||||
|
||||
override fun substituteElementToRename(element: PsiElement?, editor: Editor?): PsiElement? {
|
||||
val namedUnwrappedElement = element?.namedUnwrappedElement ?: return null
|
||||
|
||||
val callableDeclaration = namedUnwrappedElement as? KtCallableDeclaration
|
||||
?: throw IllegalStateException("Can't be for element $element there because of canProcessElement()")
|
||||
|
||||
val declarationToRename = chooseCallableToRename(callableDeclaration) ?: return null
|
||||
|
||||
val (getterJvmName, setterJvmName) = getJvmNames(namedUnwrappedElement)
|
||||
if (element is KtLightMethod) {
|
||||
val name = element.name
|
||||
if (element.name != getterJvmName && element.name != setterJvmName) return declarationToRename
|
||||
return declarationToRename.toLightMethods().firstOrNull { it.name == name }
|
||||
}
|
||||
|
||||
return declarationToRename
|
||||
}
|
||||
|
||||
override fun substituteElementToRename(element: PsiElement, editor: Editor, renameCallback: Pass<PsiElement>) {
|
||||
val namedUnwrappedElement = element.namedUnwrappedElement ?: return
|
||||
|
||||
val callableDeclaration = namedUnwrappedElement as? KtCallableDeclaration
|
||||
?: throw IllegalStateException("Can't be for element $element there because of canProcessElement()")
|
||||
|
||||
fun preprocessAndPass(substitutedJavaElement: PsiElement) {
|
||||
val (getterJvmName, setterJvmName) = getJvmNames(namedUnwrappedElement)
|
||||
val elementToProcess = if (element is KtLightMethod) {
|
||||
val name = element.name
|
||||
if (element.name != getterJvmName && element.name != setterJvmName) {
|
||||
substitutedJavaElement
|
||||
}
|
||||
else {
|
||||
substitutedJavaElement.toLightMethods().firstOrNull { it.name == name }
|
||||
}
|
||||
}
|
||||
else substitutedJavaElement
|
||||
renameCallback.pass(elementToProcess)
|
||||
}
|
||||
|
||||
val deepestSuperDeclaration = findDeepestOverriddenDeclaration(callableDeclaration)
|
||||
if (deepestSuperDeclaration == null || deepestSuperDeclaration == callableDeclaration) {
|
||||
return preprocessAndPass(callableDeclaration)
|
||||
}
|
||||
|
||||
val superPsiMethods = listOfNotNull(deepestSuperDeclaration.getRepresentativeLightMethod())
|
||||
checkSuperMethodsWithPopup(callableDeclaration, superPsiMethods, "Rename", editor) {
|
||||
preprocessAndPass(if (it.size > 1) deepestSuperDeclaration else callableDeclaration)
|
||||
}
|
||||
}
|
||||
|
||||
class PropertyMethodWrapper(private val propertyMethod: PsiMethod) : PsiNamedElement by propertyMethod, NavigationItem by propertyMethod {
|
||||
override fun getName() = propertyMethod.name
|
||||
override fun setName(name: String) = this
|
||||
override fun copy() = this
|
||||
}
|
||||
|
||||
override fun prepareRenaming(element: PsiElement, newName: String?, allRenames: MutableMap<PsiElement, String>, scope: SearchScope) {
|
||||
super.prepareRenaming(element, newName, allRenames, scope)
|
||||
|
||||
val namedUnwrappedElement = element.namedUnwrappedElement
|
||||
val propertyMethods = when(namedUnwrappedElement) {
|
||||
is KtProperty -> runReadAction { LightClassUtil.getLightClassPropertyMethods(namedUnwrappedElement) }
|
||||
is KtParameter -> runReadAction { LightClassUtil.getLightClassPropertyMethods(namedUnwrappedElement) }
|
||||
else -> throw IllegalStateException("Can't be for element $element there because of canProcessElement()")
|
||||
}
|
||||
|
||||
val newPropertyName = if (newName != null && element is KtLightMethod) propertyNameByAccessor(newName, element) else newName
|
||||
|
||||
val (getterJvmName, setterJvmName) = getJvmNames(namedUnwrappedElement)
|
||||
|
||||
val getter = propertyMethods.getter as? KtLightMethod
|
||||
val setter = propertyMethods.setter as? KtLightMethod
|
||||
if (newPropertyName != null
|
||||
&& getter != null && setter != null
|
||||
&& (element == getter || element == setter)
|
||||
&& propertyNameByAccessor(getter.name, getter) == propertyNameByAccessor(setter.name, setter)) {
|
||||
val accessorToRename = if (element == getter) setter else getter
|
||||
val newAccessorName = if (element == getter) JvmAbi.setterName(newPropertyName) else JvmAbi.getterName(newPropertyName)
|
||||
if (ApplicationManager.getApplication().isUnitTestMode
|
||||
|| Messages.showYesNoDialog("Do you want to rename ${accessorToRename.name}() as well?",
|
||||
"Rename",
|
||||
Messages.getQuestionIcon()) == Messages.YES) {
|
||||
allRenames[accessorToRename] = newAccessorName
|
||||
}
|
||||
}
|
||||
|
||||
for (propertyMethod in propertyMethods) {
|
||||
if (element is KtDeclaration && newPropertyName != null) {
|
||||
val wrapper = PropertyMethodWrapper(propertyMethod)
|
||||
when {
|
||||
JvmAbi.isGetterName(propertyMethod.name) && getterJvmName == null ->
|
||||
allRenames[wrapper] = JvmAbi.getterName(newPropertyName)
|
||||
JvmAbi.isSetterName(propertyMethod.name) && setterJvmName == null ->
|
||||
allRenames[wrapper] = JvmAbi.setterName(newPropertyName)
|
||||
}
|
||||
}
|
||||
addRenameElements(propertyMethod, (element as PsiNamedElement).name, newPropertyName, allRenames, scope)
|
||||
}
|
||||
}
|
||||
|
||||
private enum class UsageKind {
|
||||
SIMPLE_PROPERTY_USAGE,
|
||||
GETTER_USAGE,
|
||||
SETTER_USAGE
|
||||
}
|
||||
|
||||
override tailrec fun renameElement(element: PsiElement, newName: String, usages: Array<UsageInfo>, listener: RefactoringElementListener?) {
|
||||
val newNameUnquoted = newName.unquote()
|
||||
if (element is KtLightMethod) {
|
||||
if (element.modifierList.findAnnotation(DescriptorUtils.JVM_NAME.asString()) != null) {
|
||||
return super.renameElement(element, newName, usages, listener)
|
||||
}
|
||||
|
||||
val origin = element.kotlinOrigin
|
||||
val newPropertyName = propertyNameByAccessor(newNameUnquoted, element)
|
||||
// Kotlin references to Kotlin property should not use accessor name
|
||||
if (newPropertyName != null && (origin is KtProperty || origin is KtParameter)) {
|
||||
val (ktUsages, otherUsages) = usages.partition { it.reference is KtSimpleNameReference }
|
||||
super.renameElement(element, newName, otherUsages.toTypedArray(), listener)
|
||||
renameElement(origin, newPropertyName.quoteIfNeeded(), ktUsages.toTypedArray(), listener)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if (element !is KtProperty && element !is KtParameter) {
|
||||
super.renameElement(element, newName, usages, listener)
|
||||
return
|
||||
}
|
||||
|
||||
val name = (element as KtNamedDeclaration).name!!
|
||||
val oldGetterName = JvmAbi.getterName(name)
|
||||
val oldSetterName = JvmAbi.setterName(name)
|
||||
|
||||
if (isEnumCompanionPropertyWithEntryConflict(element, newNameUnquoted)) {
|
||||
for ((i, usage) in usages.withIndex()) {
|
||||
if (usage !is MoveRenameUsageInfo) continue
|
||||
val ref = usage.reference ?: continue
|
||||
// TODO: Enum value can't be accessed from Java in case of conflict with companion member
|
||||
if (ref is KtReference) {
|
||||
val newRef = (ref.bindToElement(element) as? KtSimpleNameExpression)?.mainReference ?: continue
|
||||
usages[i] = MoveRenameUsageInfo(newRef, usage.referencedElement)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val adjustedUsages = if (element is KtParameter) usages.filterNot {
|
||||
val refTarget = it.reference?.resolve()
|
||||
refTarget is KtLightMethod && DataClassDescriptorResolver.isComponentLike(Name.guessByFirstCharacter(refTarget.name))
|
||||
} else usages.toList()
|
||||
|
||||
val refKindUsages = adjustedUsages.groupBy { usage: UsageInfo ->
|
||||
val refElement = usage.reference?.resolve()
|
||||
if (refElement is PsiMethod) {
|
||||
when (refElement.name) {
|
||||
oldGetterName -> UsageKind.GETTER_USAGE
|
||||
oldSetterName -> UsageKind.SETTER_USAGE
|
||||
else -> UsageKind.SIMPLE_PROPERTY_USAGE
|
||||
}
|
||||
}
|
||||
else {
|
||||
UsageKind.SIMPLE_PROPERTY_USAGE
|
||||
}
|
||||
}
|
||||
|
||||
super.renameElement(element, JvmAbi.setterName(newNameUnquoted).quoteIfNeeded(),
|
||||
refKindUsages[UsageKind.SETTER_USAGE]?.toTypedArray() ?: arrayOf<UsageInfo>(),
|
||||
null)
|
||||
|
||||
super.renameElement(element, JvmAbi.getterName(newNameUnquoted).quoteIfNeeded(),
|
||||
refKindUsages[UsageKind.GETTER_USAGE]?.toTypedArray() ?: arrayOf<UsageInfo>(),
|
||||
null)
|
||||
|
||||
super.renameElement(element, newName,
|
||||
refKindUsages[UsageKind.SIMPLE_PROPERTY_USAGE]?.toTypedArray() ?: arrayOf<UsageInfo>(),
|
||||
null)
|
||||
|
||||
usages.forEach { (it as? KtResolvableCollisionUsageInfo)?.apply() }
|
||||
|
||||
dropOverrideKeywordIfNecessary(element)
|
||||
|
||||
listener?.elementRenamed(element)
|
||||
}
|
||||
|
||||
private fun addRenameElements(psiMethod: PsiMethod?,
|
||||
oldName: String?,
|
||||
newName: String?,
|
||||
allRenames: MutableMap<PsiElement, String>,
|
||||
scope: SearchScope) {
|
||||
if (psiMethod == null) return
|
||||
|
||||
OverridingMethodsSearch.search(psiMethod, scope, true).forEach { overrider ->
|
||||
val overriderElement = overrider.namedUnwrappedElement
|
||||
|
||||
if (overriderElement != null && overriderElement !is SyntheticElement) {
|
||||
RenameProcessor.assertNonCompileElement(overriderElement)
|
||||
|
||||
val overriderName = overriderElement.name
|
||||
|
||||
if (overriderElement is PsiMethod) {
|
||||
if (newName != null && Name.isValidIdentifier(newName)) {
|
||||
val isGetter = overriderElement.parameterList.parametersCount == 0
|
||||
allRenames[overriderElement] = if (isGetter) JvmAbi.getterName(newName) else JvmAbi.setterName(newName)
|
||||
}
|
||||
}
|
||||
else {
|
||||
val newOverriderName = RefactoringUtil.suggestNewOverriderName(overriderName, oldName, newName)
|
||||
if (newOverriderName != null) {
|
||||
allRenames[overriderElement] = newOverriderName
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun findDeepestOverriddenDeclaration(declaration: KtCallableDeclaration): KtCallableDeclaration? {
|
||||
if (declaration.modifierList?.hasModifier(KtTokens.OVERRIDE_KEYWORD) == true) {
|
||||
val bindingContext = declaration.analyze()
|
||||
var descriptor = bindingContext[BindingContext.DECLARATION_TO_DESCRIPTOR, declaration]
|
||||
if (descriptor is ValueParameterDescriptor) {
|
||||
descriptor = bindingContext[BindingContext.VALUE_PARAMETER_AS_PROPERTY, descriptor]
|
||||
?: return declaration
|
||||
}
|
||||
|
||||
if (descriptor != null) {
|
||||
assert(descriptor is PropertyDescriptor) { "Property descriptor is expected" }
|
||||
|
||||
val supers = (descriptor as PropertyDescriptor).getDeepestSuperDeclarations()
|
||||
|
||||
// Take one of supers for now - API doesn't support substitute to several elements (IDEA-48796)
|
||||
val deepest = supers.first()
|
||||
if (deepest != descriptor) {
|
||||
val superPsiElement = DescriptorToSourceUtils.descriptorToDeclaration(deepest)
|
||||
return superPsiElement as? KtCallableDeclaration
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
}
|
||||
@@ -59,7 +59,7 @@ abstract class RenameKotlinPsiProcessor : RenamePsiElementProcessor() {
|
||||
return references
|
||||
}
|
||||
|
||||
override fun getQualifiedNameAfterRename(element: PsiElement, newName: String?, nonJava: Boolean): String? {
|
||||
override fun getQualifiedNameAfterRename(element: PsiElement, newName: String, nonJava: Boolean): String? {
|
||||
if (!nonJava) return newName
|
||||
|
||||
val qualifiedName = when (element) {
|
||||
@@ -70,9 +70,7 @@ abstract class RenameKotlinPsiProcessor : RenamePsiElementProcessor() {
|
||||
return PsiUtilCore.getQualifiedNameAfterRename(qualifiedName, newName)
|
||||
}
|
||||
|
||||
override fun prepareRenaming(element: PsiElement, newName: String?, allRenames: MutableMap<PsiElement, String>, scope: SearchScope) {
|
||||
if (newName == null) return
|
||||
|
||||
override fun prepareRenaming(element: PsiElement, newName: String, allRenames: MutableMap<PsiElement, String>, scope: SearchScope) {
|
||||
val safeNewName = newName.quoteIfNeeded()
|
||||
|
||||
if (!newName.isIdentifier()) {
|
||||
@@ -97,9 +95,7 @@ abstract class RenameKotlinPsiProcessor : RenamePsiElementProcessor() {
|
||||
&& ref.multiResolve(false).mapNotNullTo(HashSet()) { it.element?.unwrapped }.size > 1
|
||||
}
|
||||
|
||||
override fun getPostRenameCallback(element: PsiElement, newName: String?, elementListener: RefactoringElementListener?): Runnable? {
|
||||
if (newName == null) return null
|
||||
|
||||
override fun getPostRenameCallback(element: PsiElement, newName: String, elementListener: RefactoringElementListener): Runnable? {
|
||||
return Runnable {
|
||||
element.ambiguousImportUsages?.forEach {
|
||||
val ref = it.reference as? PsiPolyVariantReference ?: return@forEach
|
||||
|
||||
+124
@@ -0,0 +1,124 @@
|
||||
/*
|
||||
* 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.rename
|
||||
|
||||
import com.intellij.openapi.util.Key
|
||||
import com.intellij.psi.*
|
||||
import com.intellij.psi.search.SearchScope
|
||||
import com.intellij.psi.search.searches.MethodReferencesSearch
|
||||
import com.intellij.psi.search.searches.ReferencesSearch
|
||||
import com.intellij.psi.util.PsiUtilCore
|
||||
import com.intellij.refactoring.listeners.RefactoringElementListener
|
||||
import com.intellij.refactoring.rename.RenamePsiElementProcessor
|
||||
import com.intellij.usageView.UsageInfo
|
||||
import org.jetbrains.kotlin.asJava.namedUnwrappedElement
|
||||
import org.jetbrains.kotlin.asJava.toLightMethods
|
||||
import org.jetbrains.kotlin.asJava.unwrapped
|
||||
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.util.actualsForExpected
|
||||
import org.jetbrains.kotlin.idea.util.liftToExpected
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
|
||||
import org.jetbrains.kotlin.psi.psiUtil.isIdentifier
|
||||
import org.jetbrains.kotlin.psi.psiUtil.parents
|
||||
import org.jetbrains.kotlin.psi.psiUtil.quoteIfNeeded
|
||||
import org.jetbrains.kotlin.resolve.ImportPath
|
||||
|
||||
abstract class RenameKotlinPsiProcessor : RenamePsiElementProcessor() {
|
||||
override fun canProcessElement(element: PsiElement): Boolean = element is KtNamedDeclaration
|
||||
|
||||
override fun findReferences(element: PsiElement): Collection<PsiReference> {
|
||||
val searchParameters = KotlinReferencesSearchParameters(
|
||||
element,
|
||||
element.project.projectScope(),
|
||||
kotlinOptions = KotlinReferencesSearchOptions(searchForComponentConventions = false)
|
||||
)
|
||||
val references = ReferencesSearch.search(searchParameters).toMutableList()
|
||||
if (element is KtNamedFunction
|
||||
|| (element is KtProperty && !element.isLocal)
|
||||
|| (element is KtParameter && element.hasValOrVar())) {
|
||||
element.toLightMethods().flatMapTo(references) { MethodReferencesSearch.search(it) }
|
||||
}
|
||||
return references
|
||||
}
|
||||
|
||||
override fun getQualifiedNameAfterRename(element: PsiElement, newName: String?, nonJava: Boolean): String? {
|
||||
if (!nonJava) return newName
|
||||
|
||||
val qualifiedName = when (element) {
|
||||
is KtNamedDeclaration -> element.fqName?.asString() ?: element.name
|
||||
is PsiClass -> element.qualifiedName ?: element.name
|
||||
else -> return null
|
||||
}
|
||||
return PsiUtilCore.getQualifiedNameAfterRename(qualifiedName, newName)
|
||||
}
|
||||
|
||||
override fun prepareRenaming(element: PsiElement, newName: String?, allRenames: MutableMap<PsiElement, String>, scope: SearchScope) {
|
||||
if (newName == null) return
|
||||
|
||||
val safeNewName = newName.quoteIfNeeded()
|
||||
|
||||
if (!newName.isIdentifier()) {
|
||||
allRenames[element] = safeNewName
|
||||
}
|
||||
|
||||
val declaration = element.namedUnwrappedElement as? KtNamedDeclaration
|
||||
if (declaration != null) {
|
||||
declaration.liftToExpected()?.let { expectDeclaration ->
|
||||
allRenames[expectDeclaration] = safeNewName
|
||||
expectDeclaration.actualsForExpected().forEach { allRenames[it] = safeNewName }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected var PsiElement.ambiguousImportUsages: List<UsageInfo>? by UserDataProperty(Key.create("AMBIGUOUS_IMPORT_USAGES"))
|
||||
|
||||
protected fun UsageInfo.isAmbiguousImportUsage(): Boolean {
|
||||
val ref = reference as? PsiPolyVariantReference ?: return false
|
||||
val refElement = ref.element
|
||||
return refElement.parents.any { (it is KtImportDirective && !it.isAllUnder) || (it is PsiImportStaticStatement && !it.isOnDemand) }
|
||||
&& ref.multiResolve(false).mapNotNullTo(HashSet()) { it.element?.unwrapped }.size > 1
|
||||
}
|
||||
|
||||
override fun getPostRenameCallback(element: PsiElement, newName: String?, elementListener: RefactoringElementListener?): Runnable? {
|
||||
if (newName == null) return null
|
||||
|
||||
return Runnable {
|
||||
element.ambiguousImportUsages?.forEach {
|
||||
val ref = it.reference as? PsiPolyVariantReference ?: return@forEach
|
||||
if (ref.multiResolve(false).isEmpty()) {
|
||||
ref.handleElementRename(newName)
|
||||
}
|
||||
else {
|
||||
ref.element?.getStrictParentOfType<KtImportDirective>()?.let { importDirective ->
|
||||
val fqName = importDirective.importedFqName!!
|
||||
val newFqName = fqName.parent().child(Name.identifier(newName))
|
||||
val importList = importDirective.parent as KtImportList
|
||||
if (importList.imports.none { it.importedFqName == newFqName }) {
|
||||
val newImportDirective = KtPsiFactory(element).createImportDirective(ImportPath(newFqName, false))
|
||||
importDirective.parent.addAfter(newImportDirective, importDirective)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
element.ambiguousImportUsages = null
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-2
@@ -26,11 +26,10 @@ class RenameKotlinTypeParameterProcessor : RenameKotlinPsiProcessor() {
|
||||
|
||||
override fun findCollisions(
|
||||
element: PsiElement,
|
||||
newName: String?,
|
||||
newName: String,
|
||||
allRenames: MutableMap<out PsiElement, String>,
|
||||
result: MutableList<UsageInfo>
|
||||
) {
|
||||
if (newName == null) return
|
||||
val declaration = element as? KtTypeParameter ?: return
|
||||
val descriptor = declaration.unsafeResolveToDescriptor()
|
||||
checkRedeclarations(descriptor, newName, result)
|
||||
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* 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.refactoring.rename
|
||||
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.usageView.UsageInfo
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.unsafeResolveToDescriptor
|
||||
import org.jetbrains.kotlin.psi.KtTypeParameter
|
||||
|
||||
class RenameKotlinTypeParameterProcessor : RenameKotlinPsiProcessor() {
|
||||
override fun canProcessElement(element: PsiElement) = element is KtTypeParameter
|
||||
|
||||
override fun findCollisions(
|
||||
element: PsiElement,
|
||||
newName: String?,
|
||||
allRenames: MutableMap<out PsiElement, String>,
|
||||
result: MutableList<UsageInfo>
|
||||
) {
|
||||
if (newName == null) return
|
||||
val declaration = element as? KtTypeParameter ?: return
|
||||
val descriptor = declaration.unsafeResolveToDescriptor()
|
||||
checkRedeclarations(descriptor, newName, result)
|
||||
}
|
||||
}
|
||||
+6
-6
@@ -51,6 +51,7 @@ 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.usagesSearch.constructor
|
||||
import org.jetbrains.kotlin.idea.search.usagesSearch.processDelegationCallConstructorUsages
|
||||
import org.jetbrains.kotlin.idea.util.actualsForExpected
|
||||
import org.jetbrains.kotlin.idea.util.liftToExpected
|
||||
@@ -238,8 +239,9 @@ class KotlinSafeDeleteProcessor : JavaSafeDeleteProcessor() {
|
||||
|
||||
fun findDelegationCallUsages(element: PsiElement) {
|
||||
val constructors = when (element) {
|
||||
is PsiClass -> element.constructors
|
||||
is PsiMethod -> arrayOf(element)
|
||||
is KtClass -> element.allConstructors
|
||||
is PsiClass -> element.constructors.toList()
|
||||
is PsiMethod -> listOf(element)
|
||||
else -> return
|
||||
}
|
||||
for (constructor in constructors) {
|
||||
@@ -254,10 +256,8 @@ class KotlinSafeDeleteProcessor : JavaSafeDeleteProcessor() {
|
||||
|
||||
return when (element) {
|
||||
is KtClassOrObject -> {
|
||||
element.toLightClass()?.let { klass ->
|
||||
findDelegationCallUsages(klass)
|
||||
findUsagesByJavaProcessor(klass, false)
|
||||
} ?: findKotlinDeclarationUsages(element)
|
||||
findDelegationCallUsages(element)
|
||||
findKotlinDeclarationUsages(element)
|
||||
}
|
||||
|
||||
is KtSecondaryConstructor -> {
|
||||
|
||||
+464
@@ -0,0 +1,464 @@
|
||||
/*
|
||||
* 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.asJava.elements.KtLightField
|
||||
import org.jetbrains.kotlin.asJava.elements.KtLightFieldImpl
|
||||
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.usagesSearch.processDelegationCallConstructorUsages
|
||||
import org.jetbrains.kotlin.idea.util.actualsForExpected
|
||||
import org.jetbrains.kotlin.idea.util.liftToExpected
|
||||
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.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,
|
||||
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 elementToPassToJava = when (element) {
|
||||
is KtLightFieldImpl<*> -> object : KtLightField by element {
|
||||
// Suppress walking through initializer compiled PSI (it doesn't contain any reference expressions anyway)
|
||||
override fun getInitializer() = null
|
||||
}
|
||||
else -> element
|
||||
}
|
||||
val searchInfo = super.findUsages(elementToPassToJava, 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 -> {
|
||||
element.getRepresentativeLightMethod()?.let { method ->
|
||||
findDelegationCallUsages(method)
|
||||
findUsagesByJavaProcessor(method, false)
|
||||
} ?: findKotlinDeclarationUsages(element)
|
||||
}
|
||||
|
||||
is KtNamedFunction -> {
|
||||
if (element.isLocal) {
|
||||
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) {
|
||||
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()
|
||||
}
|
||||
|
||||
override fun prepareForDeletion(element: PsiElement) {
|
||||
if (element is KtDeclaration) {
|
||||
element.actualsForExpected().forEach {
|
||||
if (it is KtParameter) {
|
||||
(it.parent as? KtParameterList)?.removeParameter(it)
|
||||
}
|
||||
else {
|
||||
it.removeModifier(KtTokens.IMPL_KEYWORD)
|
||||
it.removeModifier(KtTokens.ACTUAL_KEYWORD)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
override fun getElementsToSearch(
|
||||
element: PsiElement, module: Module?, allElementsToDelete: Collection<PsiElement>
|
||||
): Collection<PsiElement>? {
|
||||
when (element) {
|
||||
is KtParameter -> {
|
||||
val expectParameter = element.liftToExpected() as? KtParameter
|
||||
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 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
Vendored
+1
-1
@@ -1,4 +1,4 @@
|
||||
// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintShortAlarmInspection
|
||||
// INSPECTION_CLASS: com.android.tools.idea.lint.AndroidLintShortAlarmInspection
|
||||
|
||||
import android.app.AlarmManager
|
||||
|
||||
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintShortAlarmInspection
|
||||
|
||||
import android.app.AlarmManager
|
||||
|
||||
@Suppress("UsePropertyAccessSyntax", "UNUSED_VARIABLE", "unused", "UNUSED_PARAMETER", "DEPRECATION")
|
||||
class TestAlarm {
|
||||
fun test(alarmManager: AlarmManager) {
|
||||
alarmManager.setRepeating(AlarmManager.ELAPSED_REALTIME, 5000, 60000, null); // OK
|
||||
alarmManager.setRepeating(AlarmManager.ELAPSED_REALTIME, 6000, 70000, null); // OK
|
||||
alarmManager.setRepeating(AlarmManager.ELAPSED_REALTIME, <warning descr="Value will be forced up to 5000 as of Android 5.1; don't rely on this to be exact">50</warning>, <warning descr="Value will be forced up to 60000 as of Android 5.1; don't rely on this to be exact">10</warning>, null); // ERROR
|
||||
|
||||
alarmManager.setRepeating(AlarmManager.ELAPSED_REALTIME, 5000, // ERROR
|
||||
<warning descr="Value will be forced up to 60000 as of Android 5.1; don't rely on this to be exact">OtherClass.MY_INTERVAL</warning>, null); // ERROR
|
||||
|
||||
val interval = 10;
|
||||
val interval2 = 2L * interval;
|
||||
alarmManager.setRepeating(AlarmManager.ELAPSED_REALTIME, 5000, <warning descr="Value will be forced up to 60000 as of Android 5.1; don't rely on this to be exact">interval2</warning>, null); // ERROR
|
||||
}
|
||||
|
||||
private object OtherClass {
|
||||
val MY_INTERVAL = 1000L;
|
||||
}
|
||||
}
|
||||
+34
-34
@@ -1,7 +1,7 @@
|
||||
|
||||
// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintNewApiInspection
|
||||
// INSPECTION_CLASS2: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintInlinedApiInspection
|
||||
// INSPECTION_CLASS3: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintOverrideInspection
|
||||
// INSPECTION_CLASS: com.android.tools.idea.lint.AndroidLintNewApiInspection
|
||||
// INSPECTION_CLASS2: com.android.tools.idea.lint.AndroidLintInlinedApiInspection
|
||||
// INSPECTION_CLASS3: com.android.tools.idea.lint.AndroidLintOverrideInspection
|
||||
|
||||
import android.animation.RectEvaluator
|
||||
import android.annotation.SuppressLint
|
||||
@@ -42,7 +42,7 @@ class ApiCallTest: Activity() {
|
||||
// Ok
|
||||
Bundle().getInt("")
|
||||
|
||||
View.<warning descr="Field requires API level 16 (current min is 1): `android.view.View#SYSTEM_UI_FLAG_FULLSCREEN`">SYSTEM_UI_FLAG_FULLSCREEN</warning>
|
||||
<warning descr="Field requires API level 16 (current min is 1): `android.view.View#SYSTEM_UI_FLAG_FULLSCREEN`">View.SYSTEM_UI_FLAG_FULLSCREEN</warning>
|
||||
|
||||
// Virtual call
|
||||
<error descr="Call requires API level 11 (current min is 1): android.app.Activity#getActionBar">getActionBar</error>() // API 11
|
||||
@@ -61,18 +61,18 @@ class ApiCallTest: Activity() {
|
||||
<error descr="Class requires API level 14 (current min is 1): android.widget.GridLayout">GridLayout::class</error>
|
||||
|
||||
// Field access
|
||||
val field = OpcodeInfo.<warning descr="Field requires API level 11 (current min is 1): `dalvik.bytecode.OpcodeInfo#MAXIMUM_VALUE`">MAXIMUM_VALUE</warning> // API 11
|
||||
val field = <warning descr="Field requires API level 11 (current min is 1): `dalvik.bytecode.OpcodeInfo#MAXIMUM_VALUE`">OpcodeInfo.MAXIMUM_VALUE</warning> // API 11
|
||||
|
||||
|
||||
val fillParent = LayoutParams.FILL_PARENT // API 1
|
||||
// This is a final int, which means it gets inlined
|
||||
val matchParent = LayoutParams.MATCH_PARENT // API 8
|
||||
// Field access: non final
|
||||
val batteryInfo = report!!.<error descr="Field requires API level 14 (current min is 1): `android.app.ApplicationErrorReport#batteryInfo`">batteryInfo</error>
|
||||
val batteryInfo = <error descr="Field requires API level 14 (current min is 1): `android.app.ApplicationErrorReport#batteryInfo`">report!!.batteryInfo</error>
|
||||
|
||||
// Enum access
|
||||
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.LOLLIPOP) {
|
||||
val mode = PorterDuff.Mode.<error descr="Field requires API level 11 (current min is 1): `android.graphics.PorterDuff.Mode#OVERLAY`">OVERLAY</error> // API 11
|
||||
val mode = <error descr="Field requires API level 11 (current min is 1): `android.graphics.PorterDuff.Mode#OVERLAY`">PorterDuff.Mode.OVERLAY</error> // API 11
|
||||
}
|
||||
}
|
||||
|
||||
@@ -95,7 +95,7 @@ class ApiCallTest: Activity() {
|
||||
|
||||
fun test3(rect: Rect) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD) {
|
||||
<error descr="Call requires API level 18 (current min is 1): android.animation.RectEvaluator#RectEvaluator">RectEvaluator</error>(); // ERROR
|
||||
<error descr="Call requires API level 18 (current min is 1): new android.animation.RectEvaluator">RectEvaluator()</error>; // ERROR
|
||||
}
|
||||
}
|
||||
|
||||
@@ -104,15 +104,15 @@ class ApiCallTest: Activity() {
|
||||
System.out.println("Something");
|
||||
RectEvaluator(rect); // OK
|
||||
} else {
|
||||
<error descr="Call requires API level 21 (current min is 1): android.animation.RectEvaluator#RectEvaluator">RectEvaluator</error>(rect); // ERROR
|
||||
<error descr="Call requires API level 21 (current min is 1): new android.animation.RectEvaluator">RectEvaluator(rect)</error>; // ERROR
|
||||
}
|
||||
}
|
||||
|
||||
fun test5(rect: Rect) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.CUPCAKE) {
|
||||
<error descr="Call requires API level 21 (current min is 1): android.animation.RectEvaluator#RectEvaluator">RectEvaluator</error>(rect); // ERROR
|
||||
<error descr="Call requires API level 21 (current min is 1): new android.animation.RectEvaluator">RectEvaluator(rect)</error>; // ERROR
|
||||
} else {
|
||||
<error descr="Call requires API level 21 (current min is 1): android.animation.RectEvaluator#RectEvaluator">RectEvaluator</error>(rect); // ERROR
|
||||
<error descr="Call requires API level 21 (current min is 1): new android.animation.RectEvaluator">RectEvaluator(rect)</error>; // ERROR
|
||||
}
|
||||
}
|
||||
|
||||
@@ -120,29 +120,29 @@ class ApiCallTest: Activity() {
|
||||
if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
|
||||
GridLayout(null).getOrientation(); // Not flagged
|
||||
} else {
|
||||
<error descr="Call requires API level 14 (current min is 1): android.widget.GridLayout#GridLayout">GridLayout</error>(null).<error descr="Call requires API level 14 (current min is 1): android.widget.GridLayout#getOrientation">getOrientation</error>(); // Flagged
|
||||
<error descr="Call requires API level 14 (current min is 1): new android.widget.GridLayout">GridLayout(null)</error>.<error descr="Call requires API level 14 (current min is 1): android.widget.GridLayout#getOrientation">getOrientation</error>(); // Flagged
|
||||
}
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
|
||||
GridLayout(null).getOrientation(); // Not flagged
|
||||
} else {
|
||||
<error descr="Call requires API level 14 (current min is 1): android.widget.GridLayout#GridLayout">GridLayout</error>(null).<error descr="Call requires API level 14 (current min is 1): android.widget.GridLayout#getOrientation">getOrientation</error>(); // Flagged
|
||||
<error descr="Call requires API level 14 (current min is 1): new android.widget.GridLayout">GridLayout(null)</error>.<error descr="Call requires API level 14 (current min is 1): android.widget.GridLayout#getOrientation">getOrientation</error>(); // Flagged
|
||||
}
|
||||
|
||||
if (SDK_INT >= ICE_CREAM_SANDWICH) {
|
||||
GridLayout(null).getOrientation(); // Not flagged
|
||||
} else {
|
||||
<error descr="Call requires API level 14 (current min is 1): android.widget.GridLayout#GridLayout">GridLayout</error>(null).<error descr="Call requires API level 14 (current min is 1): android.widget.GridLayout#getOrientation">getOrientation</error>(); // Flagged
|
||||
<error descr="Call requires API level 14 (current min is 1): new android.widget.GridLayout">GridLayout(null)</error>.<error descr="Call requires API level 14 (current min is 1): android.widget.GridLayout#getOrientation">getOrientation</error>(); // Flagged
|
||||
}
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
|
||||
GridLayout(null).getOrientation(); // Not flagged
|
||||
} else {
|
||||
<error descr="Call requires API level 14 (current min is 1): android.widget.GridLayout#GridLayout">GridLayout</error>(null).<error descr="Call requires API level 14 (current min is 1): android.widget.GridLayout#getOrientation">getOrientation</error>(); // Flagged
|
||||
<error descr="Call requires API level 14 (current min is 1): new android.widget.GridLayout">GridLayout(null)</error>.<error descr="Call requires API level 14 (current min is 1): android.widget.GridLayout#getOrientation">getOrientation</error>(); // Flagged
|
||||
}
|
||||
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
|
||||
<error descr="Call requires API level 14 (current min is 1): android.widget.GridLayout#GridLayout">GridLayout</error>(null).<error descr="Call requires API level 14 (current min is 1): android.widget.GridLayout#getOrientation">getOrientation</error>(); // Flagged
|
||||
<error descr="Call requires API level 14 (current min is 1): new android.widget.GridLayout">GridLayout(null)</error>.<error descr="Call requires API level 14 (current min is 1): android.widget.GridLayout#getOrientation">getOrientation</error>(); // Flagged
|
||||
} else {
|
||||
GridLayout(null).getOrientation(); // Not flagged
|
||||
}
|
||||
@@ -150,24 +150,24 @@ class ApiCallTest: Activity() {
|
||||
if (Build.VERSION.SDK_INT >= 14) {
|
||||
GridLayout(null).getOrientation(); // Not flagged
|
||||
} else {
|
||||
<error descr="Call requires API level 14 (current min is 1): android.widget.GridLayout#GridLayout">GridLayout</error>(null).<error descr="Call requires API level 14 (current min is 1): android.widget.GridLayout#getOrientation">getOrientation</error>(); // Flagged
|
||||
<error descr="Call requires API level 14 (current min is 1): new android.widget.GridLayout">GridLayout(null)</error>.<error descr="Call requires API level 14 (current min is 1): android.widget.GridLayout#getOrientation">getOrientation</error>(); // Flagged
|
||||
}
|
||||
|
||||
if (VERSION.SDK_INT >= VERSION_CODES.ICE_CREAM_SANDWICH) {
|
||||
GridLayout(null).getOrientation(); // Not flagged
|
||||
} else {
|
||||
<error descr="Call requires API level 14 (current min is 1): android.widget.GridLayout#GridLayout">GridLayout</error>(null).<error descr="Call requires API level 14 (current min is 1): android.widget.GridLayout#getOrientation">getOrientation</error>(); // Flagged
|
||||
<error descr="Call requires API level 14 (current min is 1): new android.widget.GridLayout">GridLayout(null)</error>.<error descr="Call requires API level 14 (current min is 1): android.widget.GridLayout#getOrientation">getOrientation</error>(); // Flagged
|
||||
}
|
||||
|
||||
// Nested conditionals
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
|
||||
if (priority) {
|
||||
<error descr="Call requires API level 14 (current min is 1): android.widget.GridLayout#GridLayout">GridLayout</error>(null).<error descr="Call requires API level 14 (current min is 1): android.widget.GridLayout#getOrientation">getOrientation</error>(); // Flagged
|
||||
<error descr="Call requires API level 14 (current min is 1): new android.widget.GridLayout">GridLayout(null)</error>.<error descr="Call requires API level 14 (current min is 1): android.widget.GridLayout#getOrientation">getOrientation</error>(); // Flagged
|
||||
} else {
|
||||
<error descr="Call requires API level 14 (current min is 1): android.widget.GridLayout#GridLayout">GridLayout</error>(null).<error descr="Call requires API level 14 (current min is 1): android.widget.GridLayout#getOrientation">getOrientation</error>(); // Flagged
|
||||
<error descr="Call requires API level 14 (current min is 1): new android.widget.GridLayout">GridLayout(null)</error>.<error descr="Call requires API level 14 (current min is 1): android.widget.GridLayout#getOrientation">getOrientation</error>(); // Flagged
|
||||
}
|
||||
} else {
|
||||
<error descr="Call requires API level 14 (current min is 1): android.widget.GridLayout#GridLayout">GridLayout</error>(null).<error descr="Call requires API level 14 (current min is 1): android.widget.GridLayout#getOrientation">getOrientation</error>(); // Flagged
|
||||
<error descr="Call requires API level 14 (current min is 1): new android.widget.GridLayout">GridLayout(null)</error>.<error descr="Call requires API level 14 (current min is 1): android.widget.GridLayout#getOrientation">getOrientation</error>(); // Flagged
|
||||
}
|
||||
|
||||
// Nested conditionals 2
|
||||
@@ -178,7 +178,7 @@ class ApiCallTest: Activity() {
|
||||
GridLayout(null).getOrientation(); // Not flagged
|
||||
}
|
||||
} else {
|
||||
<error descr="Call requires API level 14 (current min is 1): android.widget.GridLayout#GridLayout">GridLayout</error>(null); // Flagged
|
||||
<error descr="Call requires API level 14 (current min is 1): new android.widget.GridLayout">GridLayout(null)</error>; // Flagged
|
||||
}
|
||||
}
|
||||
|
||||
@@ -186,41 +186,41 @@ class ApiCallTest: Activity() {
|
||||
if (android.os.Build.VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN) {
|
||||
GridLayout(null).getOrientation(); // Not flagged
|
||||
} else {
|
||||
<error descr="Call requires API level 14 (current min is 1): android.widget.GridLayout#GridLayout">GridLayout</error>(null); // Flagged
|
||||
<error descr="Call requires API level 14 (current min is 1): new android.widget.GridLayout">GridLayout(null)</error>; // Flagged
|
||||
}
|
||||
|
||||
if (android.os.Build.VERSION.SDK_INT >= 16) {
|
||||
GridLayout(null).getOrientation(); // Not flagged
|
||||
} else {
|
||||
<error descr="Call requires API level 14 (current min is 1): android.widget.GridLayout#GridLayout">GridLayout</error>(null); // Flagged
|
||||
<error descr="Call requires API level 14 (current min is 1): new android.widget.GridLayout">GridLayout(null)</error>; // Flagged
|
||||
}
|
||||
|
||||
if (android.os.Build.VERSION.SDK_INT >= 13) {
|
||||
<error descr="Call requires API level 14 (current min is 1): android.widget.GridLayout#GridLayout">GridLayout</error>(null).<error descr="Call requires API level 14 (current min is 1): android.widget.GridLayout#getOrientation">getOrientation</error>(); // Flagged
|
||||
<error descr="Call requires API level 14 (current min is 1): new android.widget.GridLayout">GridLayout(null)</error>.<error descr="Call requires API level 14 (current min is 1): android.widget.GridLayout#getOrientation">getOrientation</error>(); // Flagged
|
||||
} else {
|
||||
<error descr="Call requires API level 14 (current min is 1): android.widget.GridLayout#GridLayout">GridLayout</error>(null); // Flagged
|
||||
<error descr="Call requires API level 14 (current min is 1): new android.widget.GridLayout">GridLayout(null)</error>; // Flagged
|
||||
}
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
|
||||
GridLayout(null).getOrientation(); // Not flagged
|
||||
} else {
|
||||
<error descr="Call requires API level 14 (current min is 1): android.widget.GridLayout#GridLayout">GridLayout</error>(null); // Flagged
|
||||
<error descr="Call requires API level 14 (current min is 1): new android.widget.GridLayout">GridLayout(null)</error>; // Flagged
|
||||
}
|
||||
|
||||
if (SDK_INT >= JELLY_BEAN) {
|
||||
GridLayout(null).getOrientation(); // Not flagged
|
||||
} else {
|
||||
<error descr="Call requires API level 14 (current min is 1): android.widget.GridLayout#GridLayout">GridLayout</error>(null); // Flagged
|
||||
<error descr="Call requires API level 14 (current min is 1): new android.widget.GridLayout">GridLayout(null)</error>; // Flagged
|
||||
}
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
|
||||
GridLayout(null).getOrientation(); // Not flagged
|
||||
} else {
|
||||
<error descr="Call requires API level 14 (current min is 1): android.widget.GridLayout#GridLayout">GridLayout</error>(null); // Flagged
|
||||
<error descr="Call requires API level 14 (current min is 1): new android.widget.GridLayout">GridLayout(null)</error>; // Flagged
|
||||
}
|
||||
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
|
||||
<error descr="Call requires API level 14 (current min is 1): android.widget.GridLayout#GridLayout">GridLayout</error>(null); // Flagged
|
||||
<error descr="Call requires API level 14 (current min is 1): new android.widget.GridLayout">GridLayout(null)</error>; // Flagged
|
||||
} else {
|
||||
GridLayout(null).getOrientation(); // Not flagged
|
||||
}
|
||||
@@ -228,13 +228,13 @@ class ApiCallTest: Activity() {
|
||||
if (Build.VERSION.SDK_INT >= 16) {
|
||||
GridLayout(null).getOrientation(); // Not flagged
|
||||
} else {
|
||||
<error descr="Call requires API level 14 (current min is 1): android.widget.GridLayout#GridLayout">GridLayout</error>(null); // Flagged
|
||||
<error descr="Call requires API level 14 (current min is 1): new android.widget.GridLayout">GridLayout(null)</error>; // Flagged
|
||||
}
|
||||
|
||||
if (VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN) {
|
||||
GridLayout(null).getOrientation(); // Not flagged
|
||||
} else {
|
||||
<error descr="Call requires API level 14 (current min is 1): android.widget.GridLayout#GridLayout">GridLayout</error>(null); // Flagged
|
||||
<error descr="Call requires API level 14 (current min is 1): new android.widget.GridLayout">GridLayout(null)</error>; // Flagged
|
||||
}
|
||||
}
|
||||
|
||||
@@ -424,13 +424,13 @@ class ApiCallTest: Activity() {
|
||||
val linear = layout as LinearLayout // OK API 1
|
||||
}
|
||||
|
||||
abstract class ErrorVectorDravable : <error descr="Call requires API level 21 (current min is 1): android.graphics.drawable.VectorDrawable#VectorDrawable"><error descr="Class requires API level 21 (current min is 1): android.graphics.drawable.VectorDrawable">VectorDrawable</error></error>(), Parcelable
|
||||
abstract class ErrorVectorDravable : <error descr="Call requires API level 21 (current min is 1): new android.graphics.drawable.VectorDrawable"><error descr="Class requires API level 21 (current min is 1): android.graphics.drawable.VectorDrawable">VectorDrawable</error>()</error>, Parcelable
|
||||
|
||||
@TargetApi(21)
|
||||
class MyVectorDravable : VectorDrawable()
|
||||
|
||||
fun testTypes() {
|
||||
<error descr="Call requires API level 14 (current min is 1): android.widget.GridLayout#GridLayout">GridLayout</error>(this)
|
||||
<error descr="Call requires API level 14 (current min is 1): new android.widget.GridLayout">GridLayout(this)</error>
|
||||
val c = <error descr="Class requires API level 21 (current min is 1): android.graphics.drawable.VectorDrawable">VectorDrawable::class</error>.java
|
||||
}
|
||||
|
||||
|
||||
+486
@@ -0,0 +1,486 @@
|
||||
|
||||
// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintNewApiInspection
|
||||
// INSPECTION_CLASS2: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintInlinedApiInspection
|
||||
// INSPECTION_CLASS3: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintOverrideInspection
|
||||
|
||||
import android.animation.RectEvaluator
|
||||
import android.annotation.SuppressLint
|
||||
import android.annotation.TargetApi
|
||||
import org.w3c.dom.DOMError
|
||||
import org.w3c.dom.DOMErrorHandler
|
||||
import org.w3c.dom.DOMLocator
|
||||
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.ViewGroup.LayoutParams
|
||||
import android.app.Activity
|
||||
import android.app.ApplicationErrorReport
|
||||
import android.graphics.drawable.VectorDrawable
|
||||
import android.graphics.Path
|
||||
import android.graphics.PorterDuff
|
||||
import android.graphics.Rect
|
||||
import android.os.Build
|
||||
import android.widget.*
|
||||
import dalvik.bytecode.OpcodeInfo
|
||||
|
||||
import android.os.Build.VERSION
|
||||
import <warning descr="Field requires API level 4 (current min is 1): `android.os.Build.VERSION#SDK_INT`">android.os.Build.VERSION.SDK_INT</warning>
|
||||
import android.os.Build.VERSION_CODES
|
||||
import android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH
|
||||
import android.os.Build.VERSION_CODES.JELLY_BEAN
|
||||
import android.os.Bundle
|
||||
import android.os.Parcelable
|
||||
import android.system.ErrnoException
|
||||
import android.widget.TextView
|
||||
|
||||
@Suppress("SENSELESS_COMPARISON", "UNUSED_EXPRESSION", "UsePropertyAccessSyntax", "UNUSED_VARIABLE", "unused", "UNUSED_PARAMETER", "DEPRECATION", "USELESS_CAST")
|
||||
class ApiCallTest: Activity() {
|
||||
|
||||
fun method(chronometer: Chronometer, locator: DOMLocator) {
|
||||
chronometer.<error descr="Call requires API level 16 (current min is 1): android.view.View#setBackground">setBackground</error>(null)
|
||||
|
||||
// Ok
|
||||
Bundle().getInt("")
|
||||
|
||||
View.<warning descr="Field requires API level 16 (current min is 1): `android.view.View#SYSTEM_UI_FLAG_FULLSCREEN`">SYSTEM_UI_FLAG_FULLSCREEN</warning>
|
||||
|
||||
// Virtual call
|
||||
<error descr="Call requires API level 11 (current min is 1): android.app.Activity#getActionBar">getActionBar</error>() // API 11
|
||||
<error descr="Call requires API level 11 (current min is 1): android.app.Activity#getActionBar">actionBar</error> // API 11
|
||||
|
||||
// Class references (no call or field access)
|
||||
val error: DOMError? = null // API 8
|
||||
val clz = <error descr="Class requires API level 8 (current min is 1): org.w3c.dom.DOMErrorHandler">DOMErrorHandler::class</error> // API 8
|
||||
|
||||
// Method call
|
||||
chronometer.<error descr="Call requires API level 3 (current min is 1): android.widget.Chronometer#getOnChronometerTickListener">onChronometerTickListener</error> // API 3
|
||||
|
||||
// Inherited method call (from TextView
|
||||
chronometer.<error descr="Call requires API level 11 (current min is 1): android.widget.TextView#setTextIsSelectable">setTextIsSelectable</error>(true) // API 11
|
||||
|
||||
<error descr="Class requires API level 14 (current min is 1): android.widget.GridLayout">GridLayout::class</error>
|
||||
|
||||
// Field access
|
||||
val field = OpcodeInfo.<warning descr="Field requires API level 11 (current min is 1): `dalvik.bytecode.OpcodeInfo#MAXIMUM_VALUE`">MAXIMUM_VALUE</warning> // API 11
|
||||
|
||||
|
||||
val fillParent = LayoutParams.FILL_PARENT // API 1
|
||||
// This is a final int, which means it gets inlined
|
||||
val matchParent = LayoutParams.MATCH_PARENT // API 8
|
||||
// Field access: non final
|
||||
val batteryInfo = report!!.<error descr="Field requires API level 14 (current min is 1): `android.app.ApplicationErrorReport#batteryInfo`">batteryInfo</error>
|
||||
|
||||
// Enum access
|
||||
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.LOLLIPOP) {
|
||||
val mode = PorterDuff.Mode.<error descr="Field requires API level 11 (current min is 1): `android.graphics.PorterDuff.Mode#OVERLAY`">OVERLAY</error> // API 11
|
||||
}
|
||||
}
|
||||
|
||||
fun test(rect: Rect) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
RectEvaluator(rect); // OK
|
||||
}
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
if (rect != null) {
|
||||
RectEvaluator(rect); // OK
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun test2(rect: Rect) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
RectEvaluator(rect); // OK
|
||||
}
|
||||
}
|
||||
|
||||
fun test3(rect: Rect) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD) {
|
||||
<error descr="Call requires API level 18 (current min is 1): android.animation.RectEvaluator#RectEvaluator">RectEvaluator</error>(); // ERROR
|
||||
}
|
||||
}
|
||||
|
||||
fun test4(rect: Rect) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
System.out.println("Something");
|
||||
RectEvaluator(rect); // OK
|
||||
} else {
|
||||
<error descr="Call requires API level 21 (current min is 1): android.animation.RectEvaluator#RectEvaluator">RectEvaluator</error>(rect); // ERROR
|
||||
}
|
||||
}
|
||||
|
||||
fun test5(rect: Rect) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.CUPCAKE) {
|
||||
<error descr="Call requires API level 21 (current min is 1): android.animation.RectEvaluator#RectEvaluator">RectEvaluator</error>(rect); // ERROR
|
||||
} else {
|
||||
<error descr="Call requires API level 21 (current min is 1): android.animation.RectEvaluator#RectEvaluator">RectEvaluator</error>(rect); // ERROR
|
||||
}
|
||||
}
|
||||
|
||||
fun test(priority: Boolean, layout: ViewGroup) {
|
||||
if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
|
||||
GridLayout(null).getOrientation(); // Not flagged
|
||||
} else {
|
||||
<error descr="Call requires API level 14 (current min is 1): android.widget.GridLayout#GridLayout">GridLayout</error>(null).<error descr="Call requires API level 14 (current min is 1): android.widget.GridLayout#getOrientation">getOrientation</error>(); // Flagged
|
||||
}
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
|
||||
GridLayout(null).getOrientation(); // Not flagged
|
||||
} else {
|
||||
<error descr="Call requires API level 14 (current min is 1): android.widget.GridLayout#GridLayout">GridLayout</error>(null).<error descr="Call requires API level 14 (current min is 1): android.widget.GridLayout#getOrientation">getOrientation</error>(); // Flagged
|
||||
}
|
||||
|
||||
if (SDK_INT >= ICE_CREAM_SANDWICH) {
|
||||
GridLayout(null).getOrientation(); // Not flagged
|
||||
} else {
|
||||
<error descr="Call requires API level 14 (current min is 1): android.widget.GridLayout#GridLayout">GridLayout</error>(null).<error descr="Call requires API level 14 (current min is 1): android.widget.GridLayout#getOrientation">getOrientation</error>(); // Flagged
|
||||
}
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
|
||||
GridLayout(null).getOrientation(); // Not flagged
|
||||
} else {
|
||||
<error descr="Call requires API level 14 (current min is 1): android.widget.GridLayout#GridLayout">GridLayout</error>(null).<error descr="Call requires API level 14 (current min is 1): android.widget.GridLayout#getOrientation">getOrientation</error>(); // Flagged
|
||||
}
|
||||
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
|
||||
<error descr="Call requires API level 14 (current min is 1): android.widget.GridLayout#GridLayout">GridLayout</error>(null).<error descr="Call requires API level 14 (current min is 1): android.widget.GridLayout#getOrientation">getOrientation</error>(); // Flagged
|
||||
} else {
|
||||
GridLayout(null).getOrientation(); // Not flagged
|
||||
}
|
||||
|
||||
if (Build.VERSION.SDK_INT >= 14) {
|
||||
GridLayout(null).getOrientation(); // Not flagged
|
||||
} else {
|
||||
<error descr="Call requires API level 14 (current min is 1): android.widget.GridLayout#GridLayout">GridLayout</error>(null).<error descr="Call requires API level 14 (current min is 1): android.widget.GridLayout#getOrientation">getOrientation</error>(); // Flagged
|
||||
}
|
||||
|
||||
if (VERSION.SDK_INT >= VERSION_CODES.ICE_CREAM_SANDWICH) {
|
||||
GridLayout(null).getOrientation(); // Not flagged
|
||||
} else {
|
||||
<error descr="Call requires API level 14 (current min is 1): android.widget.GridLayout#GridLayout">GridLayout</error>(null).<error descr="Call requires API level 14 (current min is 1): android.widget.GridLayout#getOrientation">getOrientation</error>(); // Flagged
|
||||
}
|
||||
|
||||
// Nested conditionals
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
|
||||
if (priority) {
|
||||
<error descr="Call requires API level 14 (current min is 1): android.widget.GridLayout#GridLayout">GridLayout</error>(null).<error descr="Call requires API level 14 (current min is 1): android.widget.GridLayout#getOrientation">getOrientation</error>(); // Flagged
|
||||
} else {
|
||||
<error descr="Call requires API level 14 (current min is 1): android.widget.GridLayout#GridLayout">GridLayout</error>(null).<error descr="Call requires API level 14 (current min is 1): android.widget.GridLayout#getOrientation">getOrientation</error>(); // Flagged
|
||||
}
|
||||
} else {
|
||||
<error descr="Call requires API level 14 (current min is 1): android.widget.GridLayout#GridLayout">GridLayout</error>(null).<error descr="Call requires API level 14 (current min is 1): android.widget.GridLayout#getOrientation">getOrientation</error>(); // Flagged
|
||||
}
|
||||
|
||||
// Nested conditionals 2
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
|
||||
if (priority) {
|
||||
GridLayout(null).getOrientation(); // Not flagged
|
||||
} else {
|
||||
GridLayout(null).getOrientation(); // Not flagged
|
||||
}
|
||||
} else {
|
||||
<error descr="Call requires API level 14 (current min is 1): android.widget.GridLayout#GridLayout">GridLayout</error>(null); // Flagged
|
||||
}
|
||||
}
|
||||
|
||||
fun test2(priority: Boolean) {
|
||||
if (android.os.Build.VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN) {
|
||||
GridLayout(null).getOrientation(); // Not flagged
|
||||
} else {
|
||||
<error descr="Call requires API level 14 (current min is 1): android.widget.GridLayout#GridLayout">GridLayout</error>(null); // Flagged
|
||||
}
|
||||
|
||||
if (android.os.Build.VERSION.SDK_INT >= 16) {
|
||||
GridLayout(null).getOrientation(); // Not flagged
|
||||
} else {
|
||||
<error descr="Call requires API level 14 (current min is 1): android.widget.GridLayout#GridLayout">GridLayout</error>(null); // Flagged
|
||||
}
|
||||
|
||||
if (android.os.Build.VERSION.SDK_INT >= 13) {
|
||||
<error descr="Call requires API level 14 (current min is 1): android.widget.GridLayout#GridLayout">GridLayout</error>(null).<error descr="Call requires API level 14 (current min is 1): android.widget.GridLayout#getOrientation">getOrientation</error>(); // Flagged
|
||||
} else {
|
||||
<error descr="Call requires API level 14 (current min is 1): android.widget.GridLayout#GridLayout">GridLayout</error>(null); // Flagged
|
||||
}
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
|
||||
GridLayout(null).getOrientation(); // Not flagged
|
||||
} else {
|
||||
<error descr="Call requires API level 14 (current min is 1): android.widget.GridLayout#GridLayout">GridLayout</error>(null); // Flagged
|
||||
}
|
||||
|
||||
if (SDK_INT >= JELLY_BEAN) {
|
||||
GridLayout(null).getOrientation(); // Not flagged
|
||||
} else {
|
||||
<error descr="Call requires API level 14 (current min is 1): android.widget.GridLayout#GridLayout">GridLayout</error>(null); // Flagged
|
||||
}
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
|
||||
GridLayout(null).getOrientation(); // Not flagged
|
||||
} else {
|
||||
<error descr="Call requires API level 14 (current min is 1): android.widget.GridLayout#GridLayout">GridLayout</error>(null); // Flagged
|
||||
}
|
||||
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
|
||||
<error descr="Call requires API level 14 (current min is 1): android.widget.GridLayout#GridLayout">GridLayout</error>(null); // Flagged
|
||||
} else {
|
||||
GridLayout(null).getOrientation(); // Not flagged
|
||||
}
|
||||
|
||||
if (Build.VERSION.SDK_INT >= 16) {
|
||||
GridLayout(null).getOrientation(); // Not flagged
|
||||
} else {
|
||||
<error descr="Call requires API level 14 (current min is 1): android.widget.GridLayout#GridLayout">GridLayout</error>(null); // Flagged
|
||||
}
|
||||
|
||||
if (VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN) {
|
||||
GridLayout(null).getOrientation(); // Not flagged
|
||||
} else {
|
||||
<error descr="Call requires API level 14 (current min is 1): android.widget.GridLayout#GridLayout">GridLayout</error>(null); // Flagged
|
||||
}
|
||||
}
|
||||
|
||||
fun test(textView: TextView) {
|
||||
if (textView.<error descr="Call requires API level 14 (current min is 1): android.widget.TextView#isSuggestionsEnabled">isSuggestionsEnabled</error>()) {
|
||||
//ERROR
|
||||
}
|
||||
if (textView.<error descr="Call requires API level 14 (current min is 1): android.widget.TextView#isSuggestionsEnabled">isSuggestionsEnabled</error>) {
|
||||
//ERROR
|
||||
}
|
||||
|
||||
if (SDK_INT >= JELLY_BEAN && textView.isSuggestionsEnabled()) {
|
||||
//NO ERROR
|
||||
}
|
||||
|
||||
if (SDK_INT >= JELLY_BEAN && textView.isSuggestionsEnabled) {
|
||||
//NO ERROR
|
||||
}
|
||||
|
||||
if (SDK_INT >= JELLY_BEAN && (textView.text != "" || textView.isSuggestionsEnabled)) {
|
||||
//NO ERROR
|
||||
}
|
||||
|
||||
if (SDK_INT < JELLY_BEAN && (textView.text != "" || textView.<error descr="Call requires API level 14 (current min is 1): android.widget.TextView#isSuggestionsEnabled">isSuggestionsEnabled</error>)) {
|
||||
//ERROR
|
||||
}
|
||||
|
||||
if (SDK_INT < JELLY_BEAN && textView.<error descr="Call requires API level 14 (current min is 1): android.widget.TextView#isSuggestionsEnabled">isSuggestionsEnabled</error>()) {
|
||||
//ERROR
|
||||
}
|
||||
|
||||
if (SDK_INT < JELLY_BEAN && textView.<error descr="Call requires API level 14 (current min is 1): android.widget.TextView#isSuggestionsEnabled">isSuggestionsEnabled</error>) {
|
||||
//ERROR
|
||||
}
|
||||
|
||||
if (SDK_INT < JELLY_BEAN || textView.isSuggestionsEnabled) {
|
||||
//NO ERROR
|
||||
}
|
||||
|
||||
if (SDK_INT > JELLY_BEAN || textView.<error descr="Call requires API level 14 (current min is 1): android.widget.TextView#isSuggestionsEnabled">isSuggestionsEnabled</error>) {
|
||||
//ERROR
|
||||
}
|
||||
|
||||
|
||||
// getActionBar() API 11
|
||||
if (SDK_INT <= 10 || getActionBar() == null) {
|
||||
//NO ERROR
|
||||
}
|
||||
|
||||
if (SDK_INT < 10 || <error descr="Call requires API level 11 (current min is 1): android.app.Activity#getActionBar">getActionBar</error>() == null) {
|
||||
//ERROR
|
||||
}
|
||||
|
||||
if (SDK_INT < 11 || getActionBar() == null) {
|
||||
//NO ERROR
|
||||
}
|
||||
|
||||
if (SDK_INT != 11 || getActionBar() == null) {
|
||||
//NO ERROR
|
||||
}
|
||||
|
||||
if (SDK_INT != 12 || <error descr="Call requires API level 11 (current min is 1): android.app.Activity#getActionBar">getActionBar</error>() == null) {
|
||||
//ERROR
|
||||
}
|
||||
|
||||
if (SDK_INT <= 11 || getActionBar() == null) {
|
||||
//NO ERROR
|
||||
}
|
||||
|
||||
if (SDK_INT < 12 || getActionBar() == null) {
|
||||
//NO ERROR
|
||||
}
|
||||
|
||||
if (SDK_INT <= 12 || getActionBar() == null) {
|
||||
//NO ERROR
|
||||
}
|
||||
|
||||
if (SDK_INT < 9 || <error descr="Call requires API level 11 (current min is 1): android.app.Activity#getActionBar">getActionBar</error>() == null) {
|
||||
//ERROR
|
||||
}
|
||||
|
||||
if (SDK_INT <= 9 || <error descr="Call requires API level 11 (current min is 1): android.app.Activity#getActionBar">getActionBar</error>() == null) {
|
||||
//ERROR
|
||||
}
|
||||
}
|
||||
|
||||
fun testReturn() {
|
||||
if (SDK_INT < 11) {
|
||||
return
|
||||
}
|
||||
|
||||
// No Error
|
||||
val actionBar = getActionBar()
|
||||
}
|
||||
|
||||
fun testThrow() {
|
||||
if (SDK_INT < 11) {
|
||||
throw IllegalStateException()
|
||||
}
|
||||
|
||||
// No Error
|
||||
val actionBar = getActionBar()
|
||||
}
|
||||
|
||||
fun testError() {
|
||||
if (SDK_INT < 11) {
|
||||
error("Api")
|
||||
}
|
||||
|
||||
// No Error
|
||||
val actionBar = getActionBar()
|
||||
}
|
||||
|
||||
fun testWithoutAnnotation(textView: TextView) {
|
||||
if (textView.<error descr="Call requires API level 14 (current min is 1): android.widget.TextView#isSuggestionsEnabled">isSuggestionsEnabled</error>()) {
|
||||
|
||||
}
|
||||
|
||||
if (textView.<error descr="Call requires API level 14 (current min is 1): android.widget.TextView#isSuggestionsEnabled">isSuggestionsEnabled</error>) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@TargetApi(JELLY_BEAN)
|
||||
fun testWithTargetApiAnnotation(textView: TextView) {
|
||||
if (textView.isSuggestionsEnabled()) {
|
||||
//NO ERROR, annotation
|
||||
}
|
||||
|
||||
if (textView.isSuggestionsEnabled) {
|
||||
//NO ERROR, annotation
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("NewApi")
|
||||
fun testWithSuppressLintAnnotation(textView: TextView) {
|
||||
if (textView.isSuggestionsEnabled()) {
|
||||
//NO ERROR, annotation
|
||||
}
|
||||
|
||||
if (textView.isSuggestionsEnabled) {
|
||||
//NO ERROR, annotation
|
||||
}
|
||||
}
|
||||
|
||||
fun testCatch() {
|
||||
try {
|
||||
|
||||
} catch (e: <error descr="Class requires API level 21 (current min is 1): android.system.ErrnoException">ErrnoException</error>) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
fun testOverload() {
|
||||
// this overloaded addOval available only on API Level 21
|
||||
Path().<error descr="Call requires API level 21 (current min is 1): android.graphics.Path#addOval">addOval</error>(0f, 0f, 0f, 0f, Path.Direction.CW)
|
||||
}
|
||||
|
||||
// KT-14737 False error with short-circuit evaluation
|
||||
fun testShortCircuitEvaluation() {
|
||||
<error descr="Call requires API level 21 (current min is 1): android.content.Context#getDrawable">getDrawable</error>(0) // error here as expected
|
||||
if(Build.VERSION.SDK_INT >= 23
|
||||
&& null == getDrawable(0)) // error here should not occur
|
||||
{
|
||||
getDrawable(0) // no error here as expected
|
||||
}
|
||||
}
|
||||
|
||||
// KT-1482 Kotlin Lint: "Calling new methods on older versions" does not report call on receiver in extension function
|
||||
private fun Bundle.caseE1a() { <error descr="Call requires API level 18 (current min is 1): android.os.Bundle#getBinder">getBinder</error>("") }
|
||||
|
||||
private fun Bundle.caseE1c() { this.<error descr="Call requires API level 18 (current min is 1): android.os.Bundle#getBinder">getBinder</error>("") }
|
||||
|
||||
private fun caseE1b(bundle: Bundle) { bundle.<error descr="Call requires API level 18 (current min is 1): android.os.Bundle#getBinder">getBinder</error>("") }
|
||||
|
||||
// KT-12023 Kotlin Lint: Cast doesn't trigger minSdk error
|
||||
fun testCast(layout: ViewGroup) {
|
||||
if (layout is LinearLayout) {} // OK API 1
|
||||
layout as? LinearLayout // OK API 1
|
||||
layout as LinearLayout // OK API 1
|
||||
|
||||
if (layout !is <error descr="Class requires API level 14 (current min is 1): android.widget.GridLayout">GridLayout</error>) {}
|
||||
layout as? <error descr="Class requires API level 14 (current min is 1): android.widget.GridLayout">GridLayout</error>
|
||||
layout as <error descr="Class requires API level 14 (current min is 1): android.widget.GridLayout">GridLayout</error>
|
||||
|
||||
val grid = layout as? <error descr="Class requires API level 14 (current min is 1): android.widget.GridLayout">GridLayout</error>
|
||||
val linear = layout as LinearLayout // OK API 1
|
||||
}
|
||||
|
||||
abstract class ErrorVectorDravable : <error descr="Call requires API level 21 (current min is 1): android.graphics.drawable.VectorDrawable#VectorDrawable"><error descr="Class requires API level 21 (current min is 1): android.graphics.drawable.VectorDrawable">VectorDrawable</error></error>(), Parcelable
|
||||
|
||||
@TargetApi(21)
|
||||
class MyVectorDravable : VectorDrawable()
|
||||
|
||||
fun testTypes() {
|
||||
<error descr="Call requires API level 14 (current min is 1): android.widget.GridLayout#GridLayout">GridLayout</error>(this)
|
||||
val c = <error descr="Class requires API level 21 (current min is 1): android.graphics.drawable.VectorDrawable">VectorDrawable::class</error>.java
|
||||
}
|
||||
|
||||
fun testCallWithApiAnnotation(textView: TextView) {
|
||||
<error descr="Call requires API level 21 (current min is 1): ApiCallTest.MyVectorDravable#MyVectorDravable">MyVectorDravable</error>()
|
||||
<error descr="Call requires API level 16 (current min is 1): ApiCallTest#testWithTargetApiAnnotation">testWithTargetApiAnnotation</error>(textView)
|
||||
}
|
||||
|
||||
companion object : Activity() {
|
||||
fun test() {
|
||||
<error descr="Call requires API level 21 (current min is 1): android.content.Context#getDrawable">getDrawable</error>(0)
|
||||
}
|
||||
}
|
||||
|
||||
// Return type
|
||||
internal // API 14
|
||||
val gridLayout: GridLayout?
|
||||
get() = null
|
||||
|
||||
private val report: ApplicationErrorReport?
|
||||
get() = null
|
||||
}
|
||||
|
||||
object O: Activity() {
|
||||
fun test() {
|
||||
<error descr="Call requires API level 21 (current min is 1): android.content.Context#getDrawable">getDrawable</error>(0)
|
||||
}
|
||||
}
|
||||
|
||||
fun testJava8() {
|
||||
// Error, Api 24, Java8
|
||||
mutableListOf(1, 2, 3).<error descr="Call requires API level 24 (current min is 1): java.util.Collection#removeIf">removeIf</error> {
|
||||
true
|
||||
}
|
||||
|
||||
// Ok, Kotlin
|
||||
mutableListOf(1, 2, 3).removeAll {
|
||||
true
|
||||
}
|
||||
|
||||
// Error, Api 24, Java8
|
||||
mapOf(1 to 2).<error descr="Call requires API level 24 (current min is 1): java.util.Map#forEach">forEach</error> { key, value -> key + value }
|
||||
|
||||
// Ok, Kotlin
|
||||
mapOf(1 to 2).forEach { (key, value) -> key + value }
|
||||
}
|
||||
|
||||
interface WithDefault {
|
||||
// Should be ok
|
||||
fun methodWithBody() {
|
||||
return
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintMissingSuperCallInspection
|
||||
// INSPECTION_CLASS: com.android.tools.idea.lint.AndroidLintMissingSuperCallInspection
|
||||
|
||||
package android.support.annotation
|
||||
|
||||
|
||||
+97
@@ -0,0 +1,97 @@
|
||||
// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintMissingSuperCallInspection
|
||||
|
||||
package android.support.annotation
|
||||
|
||||
@Retention(AnnotationRetention.BINARY)
|
||||
@Target(AnnotationTarget.FUNCTION)
|
||||
annotation class CallSuper
|
||||
|
||||
@Suppress("UsePropertyAccessSyntax", "UNUSED_VARIABLE", "unused", "UNUSED_PARAMETER", "DEPRECATION")
|
||||
class CallSuperTest {
|
||||
private class Child : Parent() {
|
||||
override fun <error descr="Overriding method should call `super.test1`">test1</error>() {
|
||||
// ERROR
|
||||
}
|
||||
|
||||
override fun <error descr="Overriding method should call `super.test2`">test2</error>() {
|
||||
// ERROR
|
||||
}
|
||||
|
||||
override fun <error descr="Overriding method should call `super.test3`">test3</error>() {
|
||||
// ERROR
|
||||
}
|
||||
|
||||
override fun <error descr="Overriding method should call `super.test4`">test4</error>(arg: Int) {
|
||||
// ERROR
|
||||
}
|
||||
|
||||
override fun test4(arg: String) {
|
||||
// OK
|
||||
}
|
||||
|
||||
|
||||
override fun <error descr="Overriding method should call `super.test5`">test5</error>(arg1: Int, arg2: Boolean, arg3: Map<List<String>, *>, // ERROR
|
||||
arg4: Array<IntArray>, vararg arg5: Int) {
|
||||
}
|
||||
|
||||
override fun <error descr="Overriding method should call `super.test5`">test5</error>() {
|
||||
// ERROR
|
||||
super.test6() // (wrong super)
|
||||
}
|
||||
|
||||
override fun test6() {
|
||||
// OK
|
||||
val x = 5
|
||||
super.test6()
|
||||
System.out.println(x)
|
||||
}
|
||||
}
|
||||
|
||||
private open class Parent : ParentParent() {
|
||||
@CallSuper
|
||||
protected open fun test1() {
|
||||
}
|
||||
|
||||
override fun test3() {
|
||||
super.test3()
|
||||
}
|
||||
|
||||
@CallSuper
|
||||
protected open fun test4(arg: Int) {
|
||||
}
|
||||
|
||||
protected open fun test4(arg: String) {
|
||||
}
|
||||
|
||||
@CallSuper
|
||||
protected open fun test5() {
|
||||
}
|
||||
|
||||
@CallSuper
|
||||
protected open fun test5(arg1: Int, arg2: Boolean, arg3: Map<List<String>, *>,
|
||||
arg4: Array<IntArray>, vararg arg5: Int) {
|
||||
}
|
||||
}
|
||||
|
||||
private open class ParentParent : ParentParentParent() {
|
||||
@CallSuper
|
||||
protected open fun test2() {
|
||||
}
|
||||
|
||||
@CallSuper
|
||||
protected open fun test3() {
|
||||
}
|
||||
|
||||
@CallSuper
|
||||
protected open fun test6() {
|
||||
}
|
||||
|
||||
@CallSuper
|
||||
protected fun test7() {
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
private open class ParentParentParent
|
||||
}
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintRecycleInspection
|
||||
// INSPECTION_CLASS: com.android.tools.idea.lint.AndroidLintRecycleInspection
|
||||
|
||||
@file:Suppress("UNUSED_VARIABLE")
|
||||
|
||||
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintRecycleInspection
|
||||
|
||||
@file:Suppress("UNUSED_VARIABLE")
|
||||
|
||||
import android.app.Activity
|
||||
import android.os.Bundle
|
||||
|
||||
class MainActivity : Activity() {
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
val cursor = contentResolver.<warning descr="This `Cursor` should be freed up after use with `#close()`">query</warning>(null, null, null, null, null)
|
||||
|
||||
// WARNING
|
||||
contentResolver.<warning descr="This `Cursor` should be freed up after use with `#close()`">query</warning>(null, null, null, null, null)
|
||||
|
||||
// OK, closed in chained call
|
||||
contentResolver.query(null, null, null, null, null).close()
|
||||
|
||||
// KT-14677: Kotlin Lint: "Missing recycle() calls" report cursor with `use()` call
|
||||
val cursorUsed = contentResolver.query(null, null, null, null, null)
|
||||
cursorUsed.use { }
|
||||
|
||||
// OK, used in chained call
|
||||
contentResolver.query(null, null, null, null, null).use {
|
||||
|
||||
}
|
||||
|
||||
// KT-13372: Android Lint for Kotlin: false positive "Cursor should be freed" inside 'if' expression
|
||||
if (true) {
|
||||
val c = contentResolver.query(null, null, null, null, null)
|
||||
c.close()
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintCommitTransactionInspection
|
||||
// INSPECTION_CLASS: com.android.tools.idea.lint.AndroidLintCommitTransactionInspection
|
||||
|
||||
@file:Suppress("UNUSED_VARIABLE")
|
||||
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintCommitTransactionInspection
|
||||
|
||||
@file:Suppress("UNUSED_VARIABLE")
|
||||
|
||||
import android.app.Activity
|
||||
import android.app.FragmentTransaction
|
||||
import android.app.FragmentManager
|
||||
import android.os.Bundle
|
||||
|
||||
class MainActivity : Activity() {
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
//OK
|
||||
val transaction = fragmentManager.beginTransaction()
|
||||
val transaction2: FragmentTransaction
|
||||
transaction2 = fragmentManager.beginTransaction()
|
||||
transaction.commit()
|
||||
transaction2.commit()
|
||||
|
||||
//WARNING
|
||||
val transaction3 = fragmentManager.<warning descr="This transaction should be completed with a `commit()` call">beginTransaction</warning>()
|
||||
|
||||
//OK
|
||||
fragmentManager.beginTransaction().commit()
|
||||
fragmentManager.beginTransaction().add(null, "A").commit()
|
||||
|
||||
//OK KT-14470
|
||||
Runnable {
|
||||
val a = fragmentManager.beginTransaction()
|
||||
a.commit()
|
||||
}
|
||||
}
|
||||
|
||||
// KT-14780: Kotlin Lint: "Missing commit() calls" false positive when the result of `commit()` is assigned or used as receiver
|
||||
fun testResultOfCommit(fm: FragmentManager) {
|
||||
val r1 = fm.beginTransaction().hide(fm.findFragmentByTag("aTag")).commit()
|
||||
val r2 = fm.beginTransaction().hide(fm.findFragmentByTag("aTag")).commit().toString()
|
||||
}
|
||||
}
|
||||
+15
-15
@@ -1,6 +1,6 @@
|
||||
// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintDrawAllocationInspection
|
||||
// INSPECTION_CLASS2: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintUseSparseArraysInspection
|
||||
// INSPECTION_CLASS3: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintUseValueOfInspection
|
||||
// INSPECTION_CLASS: com.android.tools.idea.lint.AndroidLintDrawAllocationInspection
|
||||
// INSPECTION_CLASS2: com.android.tools.idea.lint.AndroidLintUseSparseArraysInspection
|
||||
// INSPECTION_CLASS3: com.android.tools.idea.lint.AndroidLintUseValueOfInspection
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import java.util.HashMap
|
||||
@@ -23,8 +23,8 @@ class JavaPerformanceTest(context: Context, attrs: AttributeSet, defStyle: Int)
|
||||
super.onDraw(canvas)
|
||||
|
||||
// Various allocations:
|
||||
java.lang.<warning descr="Avoid object allocations during draw/layout operations (preallocate and reuse instead)">String("foo")</warning>
|
||||
val s = java.lang.<warning descr="Avoid object allocations during draw/layout operations (preallocate and reuse instead)">String("bar")</warning>
|
||||
<warning descr="Avoid object allocations during draw/layout operations (preallocate and reuse instead)">java.lang.String("foo")</warning>
|
||||
val s = <warning descr="Avoid object allocations during draw/layout operations (preallocate and reuse instead)">java.lang.String("bar")</warning>
|
||||
|
||||
// This one should not be reported:
|
||||
@SuppressLint("DrawAllocation")
|
||||
@@ -69,11 +69,11 @@ class JavaPerformanceTest(context: Context, attrs: AttributeSet, defStyle: Int)
|
||||
// Should use SparseBooleanArray
|
||||
val myBoolMap = <warning descr="Use `new SparseBooleanArray(...)` instead for better performance">HashMap<Int, Boolean>()</warning>
|
||||
// Should use SparseIntArray
|
||||
val myIntMap = java.util.<warning descr="Use new `SparseIntArray(...)` instead for better performance">HashMap<Int, Int>()</warning>
|
||||
val myIntMap = <warning descr="Use new `SparseIntArray(...)` instead for better performance">java.util.HashMap<Int, Int>()</warning>
|
||||
|
||||
// This one should not be reported:
|
||||
@SuppressLint("UseSparseArrays")
|
||||
val myOtherMap = HashMap<Int, Any>()
|
||||
val myOtherMap = <warning descr="Use `new SparseArray<Object>(...)` instead for better performance">HashMap<Int, Any>()</warning>
|
||||
}
|
||||
|
||||
protected fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int,
|
||||
@@ -100,20 +100,20 @@ class JavaPerformanceTest(context: Context, attrs: AttributeSet, defStyle: Int)
|
||||
|
||||
override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int,
|
||||
bottom: Int) {
|
||||
java.lang.<warning descr="Avoid object allocations during draw/layout operations (preallocate and reuse instead)">String("flag me")</warning>
|
||||
<warning descr="Avoid object allocations during draw/layout operations (preallocate and reuse instead)">java.lang.String("flag me")</warning>
|
||||
}
|
||||
|
||||
@SuppressWarnings("null") // not real code
|
||||
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
|
||||
java.lang.<warning descr="Avoid object allocations during draw/layout operations (preallocate and reuse instead)">String("flag me")</warning>
|
||||
<warning descr="Avoid object allocations during draw/layout operations (preallocate and reuse instead)">java.lang.String("flag me")</warning>
|
||||
|
||||
// Forbidden factory methods:
|
||||
Bitmap.<warning descr="Avoid object allocations during draw/layout operations (preallocate and reuse instead)">createBitmap(100, 100, null)</warning>
|
||||
android.graphics.Bitmap.<warning descr="Avoid object allocations during draw/layout operations (preallocate and reuse instead)">createScaledBitmap(null, 100, 100, false)</warning>
|
||||
BitmapFactory.<warning descr="Avoid object allocations during draw/layout operations (preallocate and reuse instead)">decodeFile(null)</warning>
|
||||
<warning descr="Avoid object allocations during draw/layout operations (preallocate and reuse instead)">Bitmap.createBitmap(100, 100, null)</warning>
|
||||
<warning descr="Avoid object allocations during draw/layout operations (preallocate and reuse instead)">android.graphics.Bitmap.createScaledBitmap(null, 100, 100, false)</warning>
|
||||
<warning descr="Avoid object allocations during draw/layout operations (preallocate and reuse instead)">BitmapFactory.decodeFile(null)</warning>
|
||||
val canvas: Canvas? = null
|
||||
canvas!!.<warning descr="Avoid object allocations during draw operations: Use `Canvas.getClipBounds(Rect)` instead of `Canvas.getClipBounds()` which allocates a temporary `Rect`">getClipBounds()</warning> // allocates on your behalf
|
||||
canvas.<warning descr="Avoid object allocations during draw operations: Use `Canvas.getClipBounds(Rect)` instead of `Canvas.getClipBounds()` which allocates a temporary `Rect`">clipBounds</warning> // allocates on your behalf
|
||||
<warning descr="Avoid object allocations during draw operations: Use `Canvas.getClipBounds(Rect)` instead of `Canvas.getClipBounds()` which allocates a temporary `Rect`">canvas!!.getClipBounds()</warning> // allocates on your behalf
|
||||
<warning descr="Avoid object allocations during draw operations: Use `Canvas.getClipBounds(Rect)` instead of `Canvas.getClipBounds()` which allocates a temporary `Rect`">canvas.clipBounds</warning> // allocates on your behalf
|
||||
canvas.getClipBounds(null) // NOT an error
|
||||
|
||||
val layoutWidth = width
|
||||
@@ -137,7 +137,7 @@ class JavaPerformanceTest(context: Context, attrs: AttributeSet, defStyle: Int)
|
||||
|
||||
// NOT lazy initialization
|
||||
if (!initialized || mOverlay == null) {
|
||||
java.lang.<warning descr="Avoid object allocations during draw/layout operations (preallocate and reuse instead)">String("foo")</warning>
|
||||
<warning descr="Avoid object allocations during draw/layout operations (preallocate and reuse instead)">java.lang.String("foo")</warning>
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+193
@@ -0,0 +1,193 @@
|
||||
// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintDrawAllocationInspection
|
||||
// INSPECTION_CLASS2: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintUseSparseArraysInspection
|
||||
// INSPECTION_CLASS3: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintUseValueOfInspection
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import java.util.HashMap
|
||||
import android.content.Context
|
||||
import android.graphics.*
|
||||
import android.util.AttributeSet
|
||||
import android.util.SparseArray
|
||||
import android.widget.Button
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@Suppress("UsePropertyAccessSyntax", "UNUSED_VARIABLE", "unused", "UNUSED_PARAMETER", "DEPRECATION")
|
||||
class JavaPerformanceTest(context: Context, attrs: AttributeSet, defStyle: Int) : Button(context, attrs, defStyle) {
|
||||
|
||||
private var cachedRect: Rect? = null
|
||||
private var shader: LinearGradient? = null
|
||||
private var lastHeight: Int = 0
|
||||
private var lastWidth: Int = 0
|
||||
|
||||
override fun onDraw(canvas: android.graphics.Canvas) {
|
||||
super.onDraw(canvas)
|
||||
|
||||
// Various allocations:
|
||||
java.lang.<warning descr="Avoid object allocations during draw/layout operations (preallocate and reuse instead)">String("foo")</warning>
|
||||
val s = java.lang.<warning descr="Avoid object allocations during draw/layout operations (preallocate and reuse instead)">String("bar")</warning>
|
||||
|
||||
// This one should not be reported:
|
||||
@SuppressLint("DrawAllocation")
|
||||
val i = 5
|
||||
|
||||
// Cached object initialized lazily: should not complain about these
|
||||
if (cachedRect == null) {
|
||||
cachedRect = Rect(0, 0, 100, 100)
|
||||
}
|
||||
if (cachedRect == null || cachedRect!!.width() != 50) {
|
||||
cachedRect = Rect(0, 0, 50, 100)
|
||||
}
|
||||
|
||||
val b = java.lang.Boolean.valueOf(true)!! // auto-boxing
|
||||
dummy(1, 2)
|
||||
|
||||
// Non-allocations
|
||||
super.animate()
|
||||
dummy2(1, 2)
|
||||
|
||||
// This will involve allocations, but we don't track
|
||||
// inter-procedural stuff here
|
||||
someOtherMethod()
|
||||
}
|
||||
|
||||
internal fun dummy(foo: Int?, bar: Int) {
|
||||
dummy2(foo!!, bar)
|
||||
}
|
||||
|
||||
internal fun dummy2(foo: Int, bar: Int) {
|
||||
}
|
||||
|
||||
internal fun someOtherMethod() {
|
||||
// Allocations are okay here
|
||||
java.lang.String("foo")
|
||||
val s = java.lang.String("bar")
|
||||
val b = java.lang.Boolean.valueOf(true)!! // auto-boxing
|
||||
|
||||
|
||||
// Sparse array candidates
|
||||
val myMap = <warning descr="Use `new SparseArray<String>(...)` instead for better performance">HashMap<Int, String>()</warning>
|
||||
// Should use SparseBooleanArray
|
||||
val myBoolMap = <warning descr="Use `new SparseBooleanArray(...)` instead for better performance">HashMap<Int, Boolean>()</warning>
|
||||
// Should use SparseIntArray
|
||||
val myIntMap = java.util.<warning descr="Use new `SparseIntArray(...)` instead for better performance">HashMap<Int, Int>()</warning>
|
||||
|
||||
// This one should not be reported:
|
||||
@SuppressLint("UseSparseArrays")
|
||||
val myOtherMap = HashMap<Int, Any>()
|
||||
}
|
||||
|
||||
protected fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int,
|
||||
x: Boolean) {
|
||||
// wrong signature
|
||||
java.lang.String("not an error")
|
||||
}
|
||||
|
||||
protected fun onMeasure(widthMeasureSpec: Int) {
|
||||
// wrong signature
|
||||
java.lang.String("not an error")
|
||||
}
|
||||
|
||||
protected fun onLayout(changed: Boolean, left: Int, top: Int, right: Int,
|
||||
bottom: Int, wrong: Int) {
|
||||
// wrong signature
|
||||
java.lang.String("not an error")
|
||||
}
|
||||
|
||||
protected fun onLayout(changed: Boolean, left: Int, top: Int, right: Int) {
|
||||
// wrong signature
|
||||
java.lang.String("not an error")
|
||||
}
|
||||
|
||||
override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int,
|
||||
bottom: Int) {
|
||||
java.lang.<warning descr="Avoid object allocations during draw/layout operations (preallocate and reuse instead)">String("flag me")</warning>
|
||||
}
|
||||
|
||||
@SuppressWarnings("null") // not real code
|
||||
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
|
||||
java.lang.<warning descr="Avoid object allocations during draw/layout operations (preallocate and reuse instead)">String("flag me")</warning>
|
||||
|
||||
// Forbidden factory methods:
|
||||
Bitmap.<warning descr="Avoid object allocations during draw/layout operations (preallocate and reuse instead)">createBitmap(100, 100, null)</warning>
|
||||
android.graphics.Bitmap.<warning descr="Avoid object allocations during draw/layout operations (preallocate and reuse instead)">createScaledBitmap(null, 100, 100, false)</warning>
|
||||
BitmapFactory.<warning descr="Avoid object allocations during draw/layout operations (preallocate and reuse instead)">decodeFile(null)</warning>
|
||||
val canvas: Canvas? = null
|
||||
canvas!!.<warning descr="Avoid object allocations during draw operations: Use `Canvas.getClipBounds(Rect)` instead of `Canvas.getClipBounds()` which allocates a temporary `Rect`">getClipBounds()</warning> // allocates on your behalf
|
||||
canvas.<warning descr="Avoid object allocations during draw operations: Use `Canvas.getClipBounds(Rect)` instead of `Canvas.getClipBounds()` which allocates a temporary `Rect`">clipBounds</warning> // allocates on your behalf
|
||||
canvas.getClipBounds(null) // NOT an error
|
||||
|
||||
val layoutWidth = width
|
||||
val layoutHeight = height
|
||||
if (mAllowCrop && (mOverlay == null || mOverlay!!.width != layoutWidth ||
|
||||
mOverlay!!.height != layoutHeight)) {
|
||||
mOverlay = Bitmap.createBitmap(layoutWidth, layoutHeight, Bitmap.Config.ARGB_8888)
|
||||
mOverlayCanvas = Canvas(mOverlay!!)
|
||||
}
|
||||
|
||||
if (widthMeasureSpec == 42) {
|
||||
throw IllegalStateException("Test") // NOT an allocation
|
||||
}
|
||||
|
||||
// More lazy init tests
|
||||
var initialized = false
|
||||
if (!initialized) {
|
||||
java.lang.String("foo")
|
||||
initialized = true
|
||||
}
|
||||
|
||||
// NOT lazy initialization
|
||||
if (!initialized || mOverlay == null) {
|
||||
java.lang.<warning descr="Avoid object allocations during draw/layout operations (preallocate and reuse instead)">String("foo")</warning>
|
||||
}
|
||||
}
|
||||
|
||||
internal fun factories() {
|
||||
val i1 = 42
|
||||
val l1 = 42L
|
||||
val b1 = true
|
||||
val c1 = 'c'
|
||||
val f1 = 1.0f
|
||||
val d1 = 1.0
|
||||
|
||||
// The following should not generate errors:
|
||||
val i3 = Integer.valueOf(42)
|
||||
}
|
||||
|
||||
private val mAllowCrop: Boolean = false
|
||||
private var mOverlayCanvas: Canvas? = null
|
||||
private var mOverlay: Bitmap? = null
|
||||
|
||||
override fun layout(l: Int, t: Int, r: Int, b: Int) {
|
||||
// Using "this." to reference fields
|
||||
if (this.shader == null)
|
||||
this.shader = LinearGradient(0f, 0f, width.toFloat(), 0f, intArrayOf(0), null,
|
||||
Shader.TileMode.REPEAT)
|
||||
|
||||
val width = width
|
||||
val height = height
|
||||
|
||||
if (shader == null || lastWidth != width || lastHeight != height) {
|
||||
lastWidth = width
|
||||
lastHeight = height
|
||||
|
||||
shader = LinearGradient(0f, 0f, width.toFloat(), 0f, intArrayOf(0), null, Shader.TileMode.REPEAT)
|
||||
}
|
||||
}
|
||||
|
||||
fun inefficientSparseArray() {
|
||||
<warning descr="Use `new SparseIntArray(...)` instead for better performance">SparseArray<Int>()</warning> // Use SparseIntArray instead
|
||||
SparseArray<Long>() // Use SparseLongArray instead
|
||||
<warning descr="Use `new SparseBooleanArray(...)` instead for better performance">SparseArray<Boolean>()</warning> // Use SparseBooleanArray instead
|
||||
SparseArray<Any>() // OK
|
||||
}
|
||||
|
||||
fun longSparseArray() {
|
||||
// but only minSdkVersion >= 17 or if has v4 support lib
|
||||
val myStringMap = HashMap<Long, String>()
|
||||
}
|
||||
|
||||
fun byteSparseArray() {
|
||||
// bytes easily apply to ints
|
||||
val myByteMap = <warning descr="Use `new SparseArray<String>(...)` instead for better performance">HashMap<Byte, String>()</warning>
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user