Refactoring: rename kotlin-android-plugin to idea-android
This commit is contained in:
@@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="JAVA_MODULE" version="4">
|
||||
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
||||
<exclude-output />
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/tests" isTestSource="true" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
<orderEntry type="library" exported="" name="android-plugin" level="project" />
|
||||
<orderEntry type="library" name="gradle-and-groovy-plugin" level="project" />
|
||||
<orderEntry type="library" name="idea-full" level="project" />
|
||||
<orderEntry type="module" module-name="idea" />
|
||||
<orderEntry type="module" module-name="ide-common" />
|
||||
<orderEntry type="module" module-name="idea-analysis" />
|
||||
<orderEntry type="module" module-name="util" />
|
||||
<orderEntry type="module" module-name="frontend" />
|
||||
<orderEntry type="module" module-name="frontend.java" />
|
||||
<orderEntry type="module" module-name="compiler-tests" scope="TEST" />
|
||||
</component>
|
||||
</module>
|
||||
+1
@@ -0,0 +1 @@
|
||||
org.jetbrains.kotlin.android.KotlinOutputParser
|
||||
@@ -0,0 +1,5 @@
|
||||
<html>
|
||||
<body>
|
||||
This inspection reports identifiers in Android projects which are not accepted by the Android runtime (for example, method names containing spaces).
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import com.android.ide.common.blame.parser.PatternAwareOutputParser;
|
||||
import com.android.ide.common.blame.parser.util.OutputLineReader;
|
||||
import com.android.utils.ILogger;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class KotlinOutputParser implements PatternAwareOutputParser {
|
||||
@Override
|
||||
public boolean parse(String s, OutputLineReader reader, List list, ILogger logger) {
|
||||
return KotlinOutputParserHelperKt.parse(s, reader, list, logger);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,307 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
import com.android.ide.common.blame.parser.util.OutputLineReader
|
||||
import com.android.utils.ILogger
|
||||
import com.google.common.collect.ImmutableList
|
||||
import com.intellij.openapi.util.io.FileUtilRt
|
||||
import com.intellij.openapi.util.text.StringUtil
|
||||
import java.io.File
|
||||
import java.lang.reflect.Constructor
|
||||
import java.util.*
|
||||
import java.util.regex.Pattern
|
||||
import java.lang.reflect.Array as RArray
|
||||
|
||||
fun parse(line: String, reader: OutputLineReader, messages: MutableList<Any>, logger: ILogger): Boolean {
|
||||
val colonIndex1 = line.colon()
|
||||
val severity = if (colonIndex1 >= 0) line.substringBeforeAndTrim(colonIndex1) else return false
|
||||
if (!severity.startsWithSeverityPrefix()) return false
|
||||
|
||||
val lineWoSeverity = line.substringAfterAndTrim(colonIndex1)
|
||||
val colonIndex2 = lineWoSeverity.colon().skipDriveOnWin(lineWoSeverity)
|
||||
if (colonIndex2 >= 0) {
|
||||
val path = lineWoSeverity.substringBeforeAndTrim(colonIndex2)
|
||||
val file = File(path)
|
||||
if (!file.isFile && FileUtilRt.getExtension(file.name) != "kt") {
|
||||
return addMessage(KotlinOutputParserHelper.createMessage(logger, severity, lineWoSeverity.amendNextLinesIfNeeded(reader)), messages)
|
||||
}
|
||||
|
||||
val lineWoPath = lineWoSeverity.substringAfterAndTrim(colonIndex2)
|
||||
val colonIndex3 = lineWoPath.colon()
|
||||
if (colonIndex3 >= 0) {
|
||||
val position = lineWoPath.substringBeforeAndTrim(colonIndex3)
|
||||
|
||||
val matcher = POSITION_PATTERN.matcher(position)
|
||||
val message = lineWoPath.substringAfterAndTrim(colonIndex3).amendNextLinesIfNeeded(reader)
|
||||
|
||||
if (matcher.matches()) {
|
||||
val lineNumber = matcher.group(1)
|
||||
val symbolNumber = matcher.group(2)
|
||||
if (lineNumber != null && symbolNumber != null) {
|
||||
try {
|
||||
return addMessage(KotlinOutputParserHelper.createMessage(logger, severity, message, path, lineNumber.toInt(), symbolNumber.toInt(), symbolNumber.toInt()), messages)
|
||||
}
|
||||
catch (e: NumberFormatException) {
|
||||
// ignore
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return addMessage(KotlinOutputParserHelper.createMessage(logger, severity, message), messages)
|
||||
}
|
||||
else {
|
||||
return addMessage(KotlinOutputParserHelper.createMessage(logger, severity, lineWoSeverity.amendNextLinesIfNeeded(reader)), messages)
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
private val COLON = ":"
|
||||
private val POSITION_PATTERN = Pattern.compile("\\(([0-9]*), ([0-9]*)\\)")
|
||||
|
||||
private fun String.amendNextLinesIfNeeded(reader: OutputLineReader): String {
|
||||
var nextLine = reader.readLine()
|
||||
|
||||
val builder = StringBuilder(this)
|
||||
while (nextLine != null && nextLine.isNextMessage().not()) {
|
||||
builder.append("\n").append(nextLine)
|
||||
if (!reader.hasNextLine()) break
|
||||
|
||||
nextLine = reader.readLine()
|
||||
}
|
||||
|
||||
if (nextLine != null) {
|
||||
// This code is needed for compatibility with AS 2.0 and IDEA 15.0, because of difference in android plugins
|
||||
val positionField = try {
|
||||
reader.javaClass.getDeclaredField("myPosition")
|
||||
}
|
||||
catch(e: Throwable) {
|
||||
null
|
||||
}
|
||||
if (positionField != null) {
|
||||
positionField.isAccessible = true
|
||||
positionField.setInt(reader, positionField.getInt(reader) - 1)
|
||||
}
|
||||
}
|
||||
|
||||
return builder.toString()
|
||||
}
|
||||
|
||||
private fun String.isNextMessage(): Boolean {
|
||||
val colonIndex1 = indexOf(COLON)
|
||||
return colonIndex1 == 0
|
||||
|| (colonIndex1 >= 0 && substring(0, colonIndex1).startsWithSeverityPrefix()) // Next Kotlin message
|
||||
|| StringUtil.containsIgnoreCase(this, "FAILURE")
|
||||
|| StringUtil.containsIgnoreCase(this, "FAILED")
|
||||
}
|
||||
|
||||
private fun String.startsWithSeverityPrefix(): Boolean {
|
||||
return when (this.trim()) {
|
||||
"e", "w", "i", "v" -> true
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
|
||||
private fun String.substringAfterAndTrim(index: Int) = substring(index + 1).trim()
|
||||
private fun String.substringBeforeAndTrim(index: Int) = substring(0, index).trim()
|
||||
private fun String.colon() = indexOf(COLON)
|
||||
private fun Int.skipDriveOnWin(line: String): Int {
|
||||
return if (this == 1) line.indexOf(COLON, this + 1) else this
|
||||
}
|
||||
|
||||
private fun addMessage(message: Any?, messages: MutableList<Any>): Boolean {
|
||||
if (message == null) return false
|
||||
var duplicatesPrevious = false
|
||||
val messageCount = messages.size
|
||||
if (messageCount > 0) {
|
||||
val lastMessage = messages.get(messageCount - 1)
|
||||
duplicatesPrevious = lastMessage == message
|
||||
}
|
||||
if (!duplicatesPrevious) {
|
||||
messages.add(message)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
object KotlinOutputParserHelper {
|
||||
private val isNewAndroidPlugin: Boolean
|
||||
private val packagePrefix: String
|
||||
private val severityObjectMap: HashMap<String, Any> = HashMap()
|
||||
|
||||
init {
|
||||
isNewAndroidPlugin = try {
|
||||
Class.forName("com.android.ide.common.blame.Message")
|
||||
true
|
||||
}
|
||||
catch (e: ClassNotFoundException) {
|
||||
false
|
||||
}
|
||||
|
||||
packagePrefix = if (isNewAndroidPlugin) "com.android.ide.common.blame" else "com.android.ide.common.blame.output"
|
||||
|
||||
loadSeverityEnums()
|
||||
}
|
||||
|
||||
private val simpleMessageConstructor: Constructor<*> by lazy {
|
||||
if (!isNewAndroidPlugin) {
|
||||
val messageClass = Class.forName("$packagePrefix.GradleMessage")
|
||||
val messageKindClass = Class.forName("$packagePrefix.GradleMessage\$Kind")
|
||||
messageClass.getConstructor(messageKindClass, String::class.java)
|
||||
}
|
||||
else {
|
||||
val messageClass = Class.forName("$packagePrefix.Message")
|
||||
val messageKindClass = Class.forName("$packagePrefix.Message\$Kind")
|
||||
messageClass.getConstructor(
|
||||
messageKindClass,
|
||||
String::class.java,
|
||||
String::class.java,
|
||||
ImmutableList::class.java)
|
||||
}
|
||||
}
|
||||
|
||||
private val complexMessageConstructor: Constructor<*> by lazy {
|
||||
if (!isNewAndroidPlugin) {
|
||||
val messageClass = Class.forName("$packagePrefix.GradleMessage")
|
||||
val messageKindClass = Class.forName("$packagePrefix.GradleMessage\$Kind")
|
||||
messageClass.getConstructor(
|
||||
messageKindClass,
|
||||
String::class.java, String::class.java,
|
||||
Int::class.java, Int::class.java)
|
||||
}
|
||||
else {
|
||||
val messageClass = Class.forName("$packagePrefix.Message")
|
||||
val messageKindClass = Class.forName("$packagePrefix.Message\$Kind")
|
||||
val sourceFilePositionClass = Class.forName("$packagePrefix.SourceFilePosition")
|
||||
val sourceFilePositionArrayClass = Class.forName("[L$packagePrefix.SourceFilePosition;")
|
||||
messageClass.getConstructor(
|
||||
messageKindClass,
|
||||
String::class.java,
|
||||
sourceFilePositionClass,
|
||||
sourceFilePositionArrayClass)
|
||||
}
|
||||
}
|
||||
|
||||
private val sourceFilePositionConstructor: Constructor<*> by lazy {
|
||||
assert(isNewAndroidPlugin) { "This property should be used only for New Android Plugin" }
|
||||
val sourcePositionClass = Class.forName("$packagePrefix.SourcePosition")
|
||||
val sourceFilePositionClass = Class.forName("$packagePrefix.SourceFilePosition")
|
||||
sourceFilePositionClass.getConstructor(File::class.java, sourcePositionClass)
|
||||
}
|
||||
|
||||
private val sourcePositionConstructor: Constructor<*> by lazy {
|
||||
assert(isNewAndroidPlugin) { "This property should be used only for New Android Plugin" }
|
||||
val sourcePositionClass = Class.forName("$packagePrefix.SourcePosition")
|
||||
sourcePositionClass.getConstructor(Int::class.java, Int::class.java, Int::class.java)
|
||||
}
|
||||
|
||||
private val sourcePositionVarargArg: Any by lazy {
|
||||
assert(isNewAndroidPlugin) { "This property should be used only for New Android Plugin" }
|
||||
val sourceFilePositionClass = Class.forName("$packagePrefix.SourceFilePosition")
|
||||
RArray.newInstance(sourceFilePositionClass, 0)
|
||||
}
|
||||
|
||||
private fun loadSeverityEnums() {
|
||||
val messageKindClass = if (isNewAndroidPlugin)
|
||||
Class.forName("$packagePrefix.Message\$Kind")
|
||||
else
|
||||
Class.forName("$packagePrefix.GradleMessage\$Kind")
|
||||
|
||||
val messageKindConstants = messageKindClass.enumConstants as Array<Any>
|
||||
for (kind in messageKindConstants) {
|
||||
when(kind.toString()) {
|
||||
"ERROR" -> severityObjectMap.put("e", kind)
|
||||
"WARNING" -> severityObjectMap.put("w", kind)
|
||||
"INFO" -> severityObjectMap.put("i", kind)
|
||||
"SIMPLE" -> severityObjectMap.put("v", kind)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun createMessage(
|
||||
logger: ILogger,
|
||||
severity: String,
|
||||
text: String,
|
||||
file: String? = null,
|
||||
lineNumber: Int? = null,
|
||||
columnIndex: Int? = null,
|
||||
offset: Int? = null
|
||||
): Any? {
|
||||
try {
|
||||
val severityConst: Any = severityObjectMap[severity] ?: return null
|
||||
|
||||
if (isNewAndroidPlugin) {
|
||||
return createNewMessage(severityConst, text.trim(), file, lineNumber, columnIndex, offset)
|
||||
}
|
||||
else {
|
||||
return createOldMessage(severityConst, text.trim(), file, lineNumber, columnIndex)
|
||||
}
|
||||
}
|
||||
catch(e: Throwable) {
|
||||
logger.error(e, "Exception from KotlinOutputParser")
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
private fun createNewMessage(
|
||||
severityConst: Any,
|
||||
text: String,
|
||||
file: String?,
|
||||
lineNumber: Int?,
|
||||
columnIndex: Int?,
|
||||
offset: Int?
|
||||
): Any? {
|
||||
if (file == null || lineNumber == null || columnIndex == null || offset == null) {
|
||||
return simpleMessageConstructor.newInstance(
|
||||
severityConst,
|
||||
text,
|
||||
text,
|
||||
ImmutableList.of<Any>())
|
||||
}
|
||||
else {
|
||||
val sourcePositionObj = sourcePositionConstructor.newInstance(lineNumber - 1, columnIndex - 1, offset)
|
||||
val sourceFilePositionObj = sourceFilePositionConstructor.newInstance(File(file), sourcePositionObj)
|
||||
|
||||
return complexMessageConstructor.newInstance(
|
||||
severityConst,
|
||||
text,
|
||||
sourceFilePositionObj,
|
||||
sourcePositionVarargArg)
|
||||
}
|
||||
}
|
||||
|
||||
private fun createOldMessage(
|
||||
severityConst: Any,
|
||||
text: String,
|
||||
file: String?,
|
||||
lineNumber: Int?,
|
||||
columnIndex: Int?
|
||||
): Any? {
|
||||
if (file == null || lineNumber == null || columnIndex == null) {
|
||||
return simpleMessageConstructor.newInstance(severityConst, text)
|
||||
}
|
||||
else {
|
||||
return complexMessageConstructor.newInstance(
|
||||
severityConst,
|
||||
text, file,
|
||||
lineNumber, columnIndex)
|
||||
}
|
||||
}
|
||||
}
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* 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.actions
|
||||
|
||||
import com.intellij.openapi.fileEditor.FileEditor
|
||||
import com.intellij.openapi.util.Key
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import com.intellij.ui.EditorNotificationPanel
|
||||
import com.intellij.ui.EditorNotifications
|
||||
|
||||
class KotlinNewActivityNotification : EditorNotifications.Provider<EditorNotificationPanel>() {
|
||||
override fun getKey() = KEY
|
||||
|
||||
override fun createNotificationPanel(file: VirtualFile, editor: FileEditor): EditorNotificationPanel? {
|
||||
if (NewKotlinActivityAction.willBeConvertedToKotlin(file)) {
|
||||
return EditorNotificationPanel().apply {
|
||||
setText("This file will be converted to Kotlin after Gradle project sync")
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val KEY = Key.create<EditorNotificationPanel>("Kotlin.new.activity.notification")
|
||||
}
|
||||
}
|
||||
+222
@@ -0,0 +1,222 @@
|
||||
/*
|
||||
* 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.actions
|
||||
|
||||
import com.android.tools.idea.gradle.GradleSyncState
|
||||
import com.android.tools.idea.gradle.project.GradleSyncListener
|
||||
import com.intellij.history.core.RevisionsCollector
|
||||
import com.intellij.history.core.revisions.Revision
|
||||
import com.intellij.history.integration.LocalHistoryImpl
|
||||
import com.intellij.openapi.actionSystem.*
|
||||
import com.intellij.openapi.diagnostic.Logger
|
||||
import com.intellij.openapi.module.Module
|
||||
import com.intellij.openapi.project.DumbService
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import com.intellij.psi.PsiJavaFile
|
||||
import org.jetbrains.android.facet.AndroidFacet
|
||||
import org.jetbrains.kotlin.idea.KotlinIcons
|
||||
import org.jetbrains.kotlin.idea.actions.JavaToKotlinAction
|
||||
import org.jetbrains.kotlin.idea.refactoring.toPsiFile
|
||||
import java.io.File
|
||||
import kotlin.reflect.functions
|
||||
import kotlin.reflect.memberFunctions
|
||||
|
||||
private val NEW_KOTLIN_ACTIVITY_START_LABEL = "Start New Kotlin Activity Action"
|
||||
private val NEW_KOTLIN_ACTIVITY_END_LABEL = "Finish New Kotlin Activity Action"
|
||||
|
||||
class NewKotlinActivityAction: AnAction(KotlinIcons.FILE) {
|
||||
|
||||
companion object {
|
||||
internal fun attachGradleSyncListener(project: Project) {
|
||||
subscribe(project, gradleSyncListener)
|
||||
}
|
||||
|
||||
internal fun willBeConvertedToKotlin(file: VirtualFile): Boolean {
|
||||
return javaFilesToKotlin?.any {
|
||||
it.virtualFile == file
|
||||
} ?: false
|
||||
}
|
||||
|
||||
private val LOG = Logger.getInstance(NewKotlinActivityAction::class.java)
|
||||
private var javaFilesToKotlin: List<PsiJavaFile>? = null
|
||||
|
||||
private fun convertFilesAfterProjectSync(files: List<PsiJavaFile>) {
|
||||
javaFilesToKotlin = files
|
||||
}
|
||||
|
||||
private val gradleSyncListener = object: GradleSyncListener.Adapter() {
|
||||
override fun syncSucceeded(project: Project) {
|
||||
convertFiles(project)
|
||||
}
|
||||
|
||||
override fun syncFailed(project: Project, errorMessage: String) {
|
||||
convertFiles(project)
|
||||
}
|
||||
|
||||
private fun convertFiles(project: Project) {
|
||||
if (javaFilesToKotlin != null) {
|
||||
DumbService.getInstance(project).smartInvokeLater {
|
||||
val filesToConvert = javaFilesToKotlin!!.filter { it.isValid }
|
||||
if (filesToConvert.isNotEmpty()) {
|
||||
JavaToKotlinAction.convertFiles(filesToConvert, project, false)
|
||||
}
|
||||
javaFilesToKotlin = null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun subscribe(project: Project, listener: GradleSyncListener) {
|
||||
try {
|
||||
val subscribeFun = GradleSyncState::class.functions.find { it.name == "subscribe" && it.parameters.count() == 2 }
|
||||
if (subscribeFun != null) {
|
||||
// AS 2.0
|
||||
subscribeFun.call(project, listener)
|
||||
}
|
||||
else {
|
||||
// AS 1.5
|
||||
val connection = project.messageBus.connect(project)
|
||||
connection.subscribe(GradleSyncState.GRADLE_SYNC_TOPIC, gradleSyncListener)
|
||||
}
|
||||
}
|
||||
catch(e: Throwable) {
|
||||
LOG.error(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun actionPerformed(e: AnActionEvent) {
|
||||
val project = e.project ?: return
|
||||
|
||||
LocalHistoryImpl.getInstanceImpl().putSystemLabel(project, NEW_KOTLIN_ACTIVITY_START_LABEL)
|
||||
val isSuccess = showWizard(e.dataContext)
|
||||
LocalHistoryImpl.getInstanceImpl().putSystemLabel(project, NEW_KOTLIN_ACTIVITY_END_LABEL)
|
||||
|
||||
if (!isSuccess) return
|
||||
|
||||
val localHistory = LocalHistoryImpl.getInstanceImpl()
|
||||
val gateway = localHistory.gateway!!
|
||||
val localHistoryFacade = localHistory.facade
|
||||
|
||||
val revisionsCollector = RevisionsCollector(
|
||||
localHistoryFacade, gateway.createTransientRootEntry(),
|
||||
project.baseDir.path, project.locationHash, null)
|
||||
|
||||
val revisions = revisionsCollector.result
|
||||
var endRevision: Revision? = null
|
||||
for (rev in revisions) {
|
||||
val label = rev.label ?: continue
|
||||
if (label == NEW_KOTLIN_ACTIVITY_END_LABEL) {
|
||||
endRevision = rev
|
||||
}
|
||||
|
||||
if (label == NEW_KOTLIN_ACTIVITY_START_LABEL && endRevision != null) {
|
||||
val javaFiles = arrayListOf<PsiJavaFile>()
|
||||
val differences = endRevision.getDifferencesWith(rev)
|
||||
for (difference in differences) {
|
||||
if (difference.right == null && difference.isFile) {
|
||||
val file = File(difference.left.path)
|
||||
if (file.extension == "java") {
|
||||
val psiFile = file.toPsiFile(project) as? PsiJavaFile
|
||||
if (psiFile != null) {
|
||||
javaFiles.add(psiFile)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (javaFiles.isNotEmpty()) {
|
||||
val syncState = GradleSyncState.getInstance(project)
|
||||
if (syncState.isSyncInProgress) {
|
||||
convertFilesAfterProjectSync(javaFiles)
|
||||
}
|
||||
else {
|
||||
JavaToKotlinAction.convertFiles(javaFiles, project, false)
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun showWizard(dataContext: DataContext): Boolean {
|
||||
try {
|
||||
val wizardClass = try {
|
||||
// AS 1.5
|
||||
Class.forName("com.android.tools.idea.wizard.NewAndroidActivityWizard")
|
||||
}
|
||||
catch(e: ClassNotFoundException) {
|
||||
// AS 2.0
|
||||
Class.forName("com.android.tools.idea.npw.NewAndroidActivityWizard")
|
||||
}
|
||||
|
||||
val constructor = wizardClass.getConstructor(Module::class.java, VirtualFile::class.java, File::class.java)
|
||||
val wizard = constructor.newInstance(
|
||||
LangDataKeys.MODULE.getData(dataContext),
|
||||
CommonDataKeys.VIRTUAL_FILE.getData(dataContext),
|
||||
null)
|
||||
|
||||
wizardClass.kotlin.functions.firstOrNull { it.name == "init" }?.call(wizard)
|
||||
return (wizardClass.kotlin.functions.firstOrNull { it.name == "showAndGet" }?.call(wizard) as? Boolean) ?: false
|
||||
}
|
||||
catch(e: Throwable) {
|
||||
LOG.error(e)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
override fun update(e: AnActionEvent) {
|
||||
val view = LangDataKeys.IDE_VIEW.getData(e.dataContext)
|
||||
val module = LangDataKeys.MODULE.getData(e.dataContext)
|
||||
val facet = if (module != null) AndroidFacet.getInstance(module) else null
|
||||
val presentation = e.presentation
|
||||
val isProjectReady = facet != null && isProjectReady(facet)
|
||||
presentation.text = "Kotlin Activity" + if (isProjectReady) "" else " (Project not ready)"
|
||||
presentation.isVisible = view != null && facet != null && isVisible(facet)
|
||||
}
|
||||
|
||||
private fun isVisible(facet: AndroidFacet): Boolean {
|
||||
try {
|
||||
val shouldSetVisible = AndroidFacet::class.memberFunctions.singleOrNull {
|
||||
it.name == "isGradleProject" || it.name == "requiresAndroidModel"
|
||||
}
|
||||
|
||||
return shouldSetVisible?.call(facet) != null
|
||||
}
|
||||
catch(e: Throwable) {
|
||||
LOG.error(e)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
private fun isProjectReady(facet: AndroidFacet): Boolean {
|
||||
try {
|
||||
val getAndroidProjectInfoFun = AndroidFacet::class.memberFunctions.singleOrNull {
|
||||
it.name == "getIdeaAndroidProject" || it.name == "getAndroidModel"
|
||||
}
|
||||
|
||||
return getAndroidProjectInfoFun?.call(facet) != null
|
||||
}
|
||||
catch(e: Throwable) {
|
||||
LOG.error(e)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
override fun hashCode() = 0
|
||||
override fun equals(other: Any?) = other is NewKotlinActivityAction
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
import com.intellij.psi.PsiElement
|
||||
import org.jetbrains.android.facet.AndroidFacet
|
||||
|
||||
fun PsiElement.getAndroidFacetForFile(): AndroidFacet? {
|
||||
val file = containingFile ?: return null
|
||||
return AndroidFacet.getInstance(file)
|
||||
}
|
||||
+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.configure;
|
||||
|
||||
import com.intellij.openapi.module.Module;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.kotlin.idea.KotlinPluginUtil;
|
||||
import org.jetbrains.kotlin.idea.configuration.KotlinWithGradleConfigurator;
|
||||
import org.jetbrains.kotlin.resolve.TargetPlatform;
|
||||
import org.jetbrains.kotlin.resolve.jvm.platform.JvmPlatform;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.GroovyFile;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrClosableBlock;
|
||||
|
||||
public class KotlinAndroidGradleModuleConfigurator extends KotlinWithGradleConfigurator {
|
||||
public static final String NAME = "android-gradle";
|
||||
|
||||
private static final String APPLY_KOTLIN_ANDROID = "apply plugin: 'kotlin-android'";
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String getName() {
|
||||
return NAME;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public TargetPlatform getTargetPlatform() {
|
||||
return JvmPlatform.INSTANCE;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String getPresentableText() {
|
||||
return "Android with Gradle";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isApplicable(@NotNull Module module) {
|
||||
return KotlinPluginUtil.isAndroidGradleModule(module);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getApplyPluginDirective() {
|
||||
return APPLY_KOTLIN_ANDROID;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean addSourceSetsBlock(@NotNull GroovyFile file) {
|
||||
GrClosableBlock androidBlock = getAndroidBlock(file);
|
||||
return addLastExpressionInBlockIfNeeded(SOURCE_SET, getSourceSetsBlock(androidBlock));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean addElementsToFile(@NotNull GroovyFile groovyFile, boolean isTopLevelProjectFile, @NotNull String version) {
|
||||
if (isTopLevelProjectFile) {
|
||||
return addElementsToProjectFile(groovyFile, version);
|
||||
}
|
||||
else {
|
||||
return addElementsToModuleFile(groovyFile, version);
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static GrClosableBlock getAndroidBlock(@NotNull GroovyFile file) {
|
||||
return getBlockOrCreate(file, "android");
|
||||
}
|
||||
|
||||
KotlinAndroidGradleModuleConfigurator() {
|
||||
}
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* 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.facet
|
||||
|
||||
import com.intellij.openapi.project.Project
|
||||
import org.jetbrains.kotlin.android.actions.NewKotlinActivityAction
|
||||
|
||||
class KotlinAndroidStartupManager(private val project: Project) {
|
||||
init {
|
||||
NewKotlinActivityAction.attachGradleSyncListener(project)
|
||||
}
|
||||
}
|
||||
+89
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* 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.inspection
|
||||
|
||||
import com.intellij.codeInsight.FileModificationService
|
||||
import com.intellij.codeInspection.*
|
||||
import com.intellij.ide.DataManager
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.fileEditor.FileEditorManager
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.PsiElementVisitor
|
||||
import com.intellij.refactoring.rename.RenameHandlerRegistry
|
||||
import org.jetbrains.kotlin.android.getAndroidFacetForFile
|
||||
import org.jetbrains.kotlin.idea.inspections.AbstractKotlinInspection
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
|
||||
class IllegalIdentifierInspection : AbstractKotlinInspection() {
|
||||
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean, session: LocalInspectionToolSession): PsiElementVisitor {
|
||||
return object : KtVisitorVoid() {
|
||||
override fun visitElement(element: PsiElement) {
|
||||
if (element.node.elementType != KtTokens.IDENTIFIER) return
|
||||
|
||||
val text = element.text
|
||||
// '`' can't be escaped now
|
||||
if (!text.startsWith('`') || !text.endsWith('`')) return
|
||||
|
||||
val unquotedName = KtPsiUtil.unquoteIdentifier(text)
|
||||
// This is already an error
|
||||
if (unquotedName.isEmpty()) return
|
||||
|
||||
if (!unquotedName.all { isValidDalvikCharacter(it) } && checkAndroidFacet(element)) {
|
||||
holder.registerProblem(element,
|
||||
"Identifier not allowed in Android projects",
|
||||
ProblemHighlightType.GENERIC_ERROR,
|
||||
RenameIdentifierFix())
|
||||
}
|
||||
}
|
||||
|
||||
fun checkAndroidFacet(element: PsiElement): Boolean {
|
||||
return element.getAndroidFacetForFile() != null || ApplicationManager.getApplication().isUnitTestMode
|
||||
}
|
||||
|
||||
// https://source.android.com/devices/tech/dalvik/dex-format.html#string-syntax
|
||||
fun isValidDalvikCharacter(c: Char) = when (c) {
|
||||
in 'A'..'Z' -> true
|
||||
in 'a'..'z' -> true
|
||||
in '0'..'9' -> true
|
||||
'$', '-', '_' -> true
|
||||
in '\u00a1' .. '\u1fff' -> true
|
||||
in '\u2010' .. '\u2027' -> true
|
||||
in '\u2030' .. '\ud7ff' -> true
|
||||
in '\ue000' .. '\uffef' -> true
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class RenameIdentifierFix : LocalQuickFix {
|
||||
override fun getName() = "Rename"
|
||||
override fun getFamilyName() = name
|
||||
|
||||
override fun applyFix(project: Project, descriptor: ProblemDescriptor) {
|
||||
val element = descriptor.psiElement ?: return
|
||||
val file = element.containingFile ?: return
|
||||
if (!FileModificationService.getInstance().prepareFileForWrite(file)) return
|
||||
val editorManager = FileEditorManager.getInstance(project)
|
||||
val editor = editorManager.getSelectedEditor(file.virtualFile) ?: return
|
||||
val dataContext = DataManager.getInstance().getDataContext(editor.component)
|
||||
val renameHandler = RenameHandlerRegistry.getInstance().getRenameHandler(dataContext)
|
||||
renameHandler?.invoke(project, arrayOf(element), dataContext);
|
||||
}
|
||||
}
|
||||
}
|
||||
+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,111 @@
|
||||
/*
|
||||
* 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 org.jetbrains.android.util.AndroidResourceUtil
|
||||
import com.intellij.psi.PsiElement
|
||||
import org.jetbrains.android.facet.AndroidFacet
|
||||
import org.jetbrains.kotlin.psi.KtSimpleNameExpression
|
||||
import com.intellij.psi.util.PsiTreeUtil
|
||||
import com.intellij.psi.PsiClass
|
||||
import org.jetbrains.android.util.AndroidUtils
|
||||
import com.android.SdkConstants
|
||||
import org.jetbrains.android.augment.AndroidPsiElementFinder
|
||||
import org.jetbrains.kotlin.idea.references.mainReference
|
||||
import org.jetbrains.kotlin.psi.KtDotQualifiedExpression
|
||||
import org.jetbrains.kotlin.psi.psiUtil.getReceiverExpression
|
||||
import org.jetbrains.kotlin.psi.KtExpression
|
||||
|
||||
fun getReferenceExpression(element: PsiElement?): KtSimpleNameExpression? {
|
||||
return PsiTreeUtil.getParentOfType<KtSimpleNameExpression>(element, KtSimpleNameExpression::class.java)
|
||||
}
|
||||
|
||||
// given 'R.a.b' returns info for all three parts of the expression 'a', 'b', 'R'
|
||||
fun getInfo(
|
||||
referenceExpression: KtSimpleNameExpression,
|
||||
facet: AndroidFacet
|
||||
): AndroidResourceUtil.MyReferredResourceFieldInfo? {
|
||||
val info = getReferredInfo(referenceExpression, facet)
|
||||
if (info != null) return info
|
||||
|
||||
val topMostQualified = referenceExpression.getParentQualified().getParentQualified() ?: return null
|
||||
val selectorCandidate = topMostQualified.selectorExpression as? KtSimpleNameExpression ?: return null
|
||||
return getReferredInfo(selectorCandidate, facet)
|
||||
}
|
||||
|
||||
private fun KtExpression?.getParentQualified(): KtDotQualifiedExpression? {
|
||||
return this?.parent as? KtDotQualifiedExpression
|
||||
}
|
||||
|
||||
// returns info if passed expression is 'b' in 'R.a.b'
|
||||
private fun getReferredInfo(
|
||||
lastPart: KtSimpleNameExpression,
|
||||
facet: AndroidFacet
|
||||
): AndroidResourceUtil.MyReferredResourceFieldInfo? {
|
||||
val resFieldName = lastPart.getReferencedName()
|
||||
if (resFieldName.isEmpty()) return null
|
||||
|
||||
val middlePart = getReceiverAsSimpleNameExpression(lastPart) ?: return null
|
||||
|
||||
val resClassName = middlePart.getReferencedName()
|
||||
if (resClassName.isEmpty()) return null
|
||||
|
||||
val firstPart = getReceiverAsSimpleNameExpression(middlePart) ?: return null
|
||||
|
||||
val resolvedClass = firstPart.mainReference.resolve() as? PsiClass ?: return null
|
||||
|
||||
//the following code is copied from
|
||||
// org.jetbrains.android.util.AndroidResourceUtil.getReferredResourceOrManifestField
|
||||
// (org.jetbrains.android.facet.AndroidFacet, com.intellij.psi.PsiReferenceExpression, java.lang.String, boolean)
|
||||
val classShortName = resolvedClass.name
|
||||
|
||||
val fromManifest = AndroidUtils.MANIFEST_CLASS_NAME == classShortName
|
||||
|
||||
if (!fromManifest && AndroidUtils.R_CLASS_NAME != classShortName) {
|
||||
return null
|
||||
}
|
||||
val qName = resolvedClass.qualifiedName
|
||||
|
||||
if (SdkConstants.CLASS_R == qName || AndroidPsiElementFinder.INTERNAL_R_CLASS_QNAME == qName) {
|
||||
return AndroidResourceUtil.MyReferredResourceFieldInfo(resClassName, resFieldName, true, false)
|
||||
}
|
||||
val containingFile = resolvedClass.containingFile ?: return null
|
||||
|
||||
val isFromCorrectFile =
|
||||
if (fromManifest) AndroidResourceUtil.isManifestJavaFile(facet, containingFile)
|
||||
else AndroidResourceUtil.isRJavaFile(facet, containingFile)
|
||||
|
||||
if (!isFromCorrectFile) {
|
||||
return null
|
||||
}
|
||||
|
||||
return AndroidResourceUtil.MyReferredResourceFieldInfo(resClassName, resFieldName, false, fromManifest)
|
||||
}
|
||||
|
||||
private fun getReceiverAsSimpleNameExpression(exp: KtSimpleNameExpression): KtSimpleNameExpression? {
|
||||
val receiver = exp.getReceiverExpression()
|
||||
return when (receiver) {
|
||||
is KtSimpleNameExpression -> {
|
||||
receiver
|
||||
}
|
||||
is KtDotQualifiedExpression -> {
|
||||
receiver.selectorExpression as? KtSimpleNameExpression
|
||||
}
|
||||
else -> null
|
||||
}
|
||||
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* 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.configure;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.kotlin.idea.configuration.AbstractConfigureProjectByChangingFileTest;
|
||||
|
||||
public abstract class AbstractConfigureProjectTest extends AbstractConfigureProjectByChangingFileTest {
|
||||
public void doTestAndroidGradle(@NotNull String path) throws Exception {
|
||||
doTest(path, path.replace("before", "after"), new KotlinAndroidGradleModuleConfigurator());
|
||||
}
|
||||
}
|
||||
+166
@@ -0,0 +1,166 @@
|
||||
/*
|
||||
* 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.configure;
|
||||
|
||||
import com.intellij.testFramework.TestDataPath;
|
||||
import org.jetbrains.kotlin.test.JUnit3RunnerWithInners;
|
||||
import org.jetbrains.kotlin.test.KotlinTestUtils;
|
||||
import org.jetbrains.kotlin.test.TestMetadata;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/** This class is generated by {@link org.jetbrains.kotlin.generators.tests.TestsPackage}. DO NOT MODIFY MANUALLY */
|
||||
@SuppressWarnings("all")
|
||||
@TestMetadata("idea/testData/configuration/android-gradle")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public class ConfigureProjectTestGenerated extends AbstractConfigureProjectTest {
|
||||
public void testAllFilesPresentInAndroid_gradle() throws Exception {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/configuration/android-gradle"), Pattern.compile("(\\w+)_before\\.gradle$"), true);
|
||||
}
|
||||
|
||||
@TestMetadata("androidStudioDefault_before.gradle")
|
||||
public void testAndroidStudioDefault() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/configuration/android-gradle/androidStudioDefault_before.gradle");
|
||||
doTestAndroidGradle(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("androidStudioDefaultShapshot_before.gradle")
|
||||
public void testAndroidStudioDefaultShapshot() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/configuration/android-gradle/androidStudioDefaultShapshot_before.gradle");
|
||||
doTestAndroidGradle(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("buildConfigs_before.gradle")
|
||||
public void testBuildConfigs() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/configuration/android-gradle/buildConfigs_before.gradle");
|
||||
doTestAndroidGradle(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("emptyDependencyList_before.gradle")
|
||||
public void testEmptyDependencyList() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/configuration/android-gradle/emptyDependencyList_before.gradle");
|
||||
doTestAndroidGradle(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("emptyFile_before.gradle")
|
||||
public void testEmptyFile() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/configuration/android-gradle/emptyFile_before.gradle");
|
||||
doTestAndroidGradle(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("helloWorld_before.gradle")
|
||||
public void testHelloWorld() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/configuration/android-gradle/helloWorld_before.gradle");
|
||||
doTestAndroidGradle(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("libraryFile_before.gradle")
|
||||
public void testLibraryFile() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/configuration/android-gradle/libraryFile_before.gradle");
|
||||
doTestAndroidGradle(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("missedApplyAndroidStatement_before.gradle")
|
||||
public void testMissedApplyAndroidStatement() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/configuration/android-gradle/missedApplyAndroidStatement_before.gradle");
|
||||
doTestAndroidGradle(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("missedBuildscriptBlock_before.gradle")
|
||||
public void testMissedBuildscriptBlock() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/configuration/android-gradle/missedBuildscriptBlock_before.gradle");
|
||||
doTestAndroidGradle(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("missedRepositoriesInBuildscriptBlock_before.gradle")
|
||||
public void testMissedRepositoriesInBuildscriptBlock() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/configuration/android-gradle/missedRepositoriesInBuildscriptBlock_before.gradle");
|
||||
doTestAndroidGradle(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("productFlavor_before.gradle")
|
||||
public void testProductFlavor() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/configuration/android-gradle/productFlavor_before.gradle");
|
||||
doTestAndroidGradle(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("idea/testData/configuration/android-gradle/gradleExamples")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public static class GradleExamples extends AbstractConfigureProjectTest {
|
||||
public void testAllFilesPresentInGradleExamples() throws Exception {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/configuration/android-gradle/gradleExamples"), Pattern.compile("(\\w+)_before\\.gradle$"), true);
|
||||
}
|
||||
|
||||
@TestMetadata("gradleExample0_before.gradle")
|
||||
public void testGradleExample0() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/configuration/android-gradle/gradleExamples/gradleExample0_before.gradle");
|
||||
doTestAndroidGradle(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("gradleExample18_before.gradle")
|
||||
public void testGradleExample18() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/configuration/android-gradle/gradleExamples/gradleExample18_before.gradle");
|
||||
doTestAndroidGradle(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("gradleExample22_before.gradle")
|
||||
public void testGradleExample22() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/configuration/android-gradle/gradleExamples/gradleExample22_before.gradle");
|
||||
doTestAndroidGradle(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("gradleExample44_before.gradle")
|
||||
public void testGradleExample44() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/configuration/android-gradle/gradleExamples/gradleExample44_before.gradle");
|
||||
doTestAndroidGradle(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("gradleExample5_before.gradle")
|
||||
public void testGradleExample5() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/configuration/android-gradle/gradleExamples/gradleExample5_before.gradle");
|
||||
doTestAndroidGradle(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("gradleExample50_before.gradle")
|
||||
public void testGradleExample50() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/configuration/android-gradle/gradleExamples/gradleExample50_before.gradle");
|
||||
doTestAndroidGradle(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("gradleExample58_before.gradle")
|
||||
public void testGradleExample58() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/configuration/android-gradle/gradleExamples/gradleExample58_before.gradle");
|
||||
doTestAndroidGradle(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("gradleExample65_before.gradle")
|
||||
public void testGradleExample65() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/configuration/android-gradle/gradleExamples/gradleExample65_before.gradle");
|
||||
doTestAndroidGradle(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("gradleExample8_before.gradle")
|
||||
public void testGradleExample8() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/configuration/android-gradle/gradleExamples/gradleExample8_before.gradle");
|
||||
doTestAndroidGradle(fileName);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user