Android Extensions: handle layout file rename

This commit is contained in:
Yan Zhulanow
2016-03-25 16:38:43 +03:00
parent 47b3394f62
commit 6e14c11280
7 changed files with 145 additions and 6 deletions
@@ -20,22 +20,40 @@ import com.intellij.psi.PsiElement
import com.intellij.psi.xml.XmlAttributeValue
import org.jetbrains.android.dom.wrappers.ValueResourceElementWrapper
import org.jetbrains.android.util.AndroidResourceUtil
import org.jetbrains.kotlin.android.synthetic.AndroidConst
import org.jetbrains.kotlin.android.synthetic.androidIdToName
import org.jetbrains.kotlin.idea.references.KtSimpleNameReference
import org.jetbrains.kotlin.plugin.references.SimpleNameReferenceExtension
import org.jetbrains.kotlin.psi.KtDotQualifiedExpression
import org.jetbrains.kotlin.psi.KtPsiFactory
class AndroidSimpleNameReferenceExtension : SimpleNameReferenceExtension {
override fun isReferenceTo(reference: KtSimpleNameReference, element: PsiElement): Boolean {
return element is ValueResourceElementWrapper && AndroidResourceUtil.isIdDeclaration(element)
override fun isReferenceTo(reference: KtSimpleNameReference, element: PsiElement) = when {
element is ValueResourceElementWrapper && AndroidResourceUtil.isIdDeclaration(element) -> true
isLayoutPackageIdentifier(reference) -> true
else -> false
}
private fun isLayoutPackageIdentifier(reference: KtSimpleNameReference): Boolean {
val probablyVariant = reference.element?.parent as? KtDotQualifiedExpression ?: return false
val probablyKAS = probablyVariant.receiverExpression as? KtDotQualifiedExpression ?: return false
return probablyKAS.receiverExpression.text == AndroidConst.SYNTHETIC_PACKAGE
}
override fun handleElementRename(reference: KtSimpleNameReference, psiFactory: KtPsiFactory, newElementName: String): PsiElement? {
val resolvedElement = reference.resolve()
if (resolvedElement !is XmlAttributeValue || !AndroidResourceUtil.isIdDeclaration(resolvedElement)) return null
val newSyntheticPropertyName = androidIdToName(newElementName) ?: return null
if (resolvedElement is XmlAttributeValue && AndroidResourceUtil.isIdDeclaration(resolvedElement)) {
val newSyntheticPropertyName = androidIdToName(newElementName) ?: return null
return psiFactory.createNameIdentifier(newSyntheticPropertyName.name)
}
else if (isLayoutPackageIdentifier(reference)) {
return if (newElementName.endsWith(".xml"))
psiFactory.createSimpleName(newElementName.dropLast(".xml".length))
else
reference.element
}
return psiFactory.createNameIdentifier(newSyntheticPropertyName.name)
return null
}
}
@@ -0,0 +1,10 @@
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:id="@+id/login"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</FrameLayout>
@@ -0,0 +1,12 @@
package com.myapp
import android.app.Activity
import android.os.Bundle
import java.io.File
import kotlinx.android.synthetic.main.layout.*
class MyActivity : Activity() {
override fun onCreate(savedInstanceState: Bundle?) {}
val button = <caret>login
}
@@ -0,0 +1,51 @@
/*
* 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.codeInsight.TargetElementUtil
import com.intellij.psi.impl.source.tree.injected.InjectedLanguageUtil
import com.intellij.refactoring.rename.RenameProcessor
import com.intellij.psi.impl.source.xml.XmlAttributeValueImpl
import com.intellij.psi.xml.XmlFile
import org.jetbrains.kotlin.android.synthetic.AndroidConst
import org.jetbrains.kotlin.psi.KtFile
abstract class AbstractAndroidLayoutRenameTest : KotlinAndroidTestCase() {
private val NEW_NAME = "new_name"
private val NEW_NAME_XML = "$NEW_NAME.xml"
fun doTest(path: String) {
val f = myFixture!!
getResourceDirs(path).forEach { myFixture.copyDirectoryToProject(it.name, it.name) }
val virtualFile = f.copyFileToProject(path + getTestName(true) + ".kt", "src/" + getTestName(true) + ".kt");
f.configureFromExistingVirtualFile(virtualFile)
val completionEditor = InjectedLanguageUtil.getEditorForInjectedLanguageNoCommit(f.editor, f.file)
val element = TargetElementUtil.findTargetElement(
completionEditor,
TargetElementUtil.REFERENCED_ELEMENT_ACCEPTED or TargetElementUtil.ELEMENT_NAME_ACCEPTED) as XmlAttributeValueImpl
val file = element.containingFile as XmlFile
RenameProcessor(f.project, file, NEW_NAME_XML, false, true).run()
(f.file as KtFile).importDirectives.any { it.importedFqName!!.asString() == AndroidConst.SYNTHETIC_PACKAGE + ".main." + NEW_NAME }
}
override fun getTestDataPath() = KotlinAndroidTestCaseBase.getPluginTestDataPathBase() + "/rename/" + getTestName(true) + "/"
}
@@ -0,0 +1,43 @@
/*
* 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.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("plugins/android-extensions/android-extensions-idea/testData/android/renameLayout")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public class AndroidLayoutRenameTestGenerated extends AbstractAndroidLayoutRenameTest {
public void testAllFilesPresentInRenameLayout() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("plugins/android-extensions/android-extensions-idea/testData/android/renameLayout"), Pattern.compile("^([^\\.]+)$"), false);
}
@TestMetadata("simple")
public void testSimple() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("plugins/android-extensions/android-extensions-idea/testData/android/renameLayout/simple/");
doTest(fileName);
}
}