Piyush Pradeepkumar
2 weeks ago
committed by
GitHub
20 changed files with 881 additions and 0 deletions
@ -0,0 +1,36 @@
|
||||
package org.jetbrains.compose.intentions |
||||
|
||||
import com.intellij.codeInsight.intention.LowPriorityAction |
||||
import com.intellij.codeInsight.intention.PsiElementBaseIntentionAction |
||||
import com.intellij.openapi.editor.Editor |
||||
import com.intellij.openapi.project.Project |
||||
import com.intellij.psi.PsiElement |
||||
import org.jetbrains.compose.intentions.utils.composableFinder.ComposableFunctionFinder |
||||
import org.jetbrains.compose.intentions.utils.composableFinder.ComposableFunctionFinderImpl |
||||
import org.jetbrains.compose.intentions.utils.getRootPsiElement.GetRootPsiElement |
||||
import org.jetbrains.compose.intentions.utils.isIntentionAvailable |
||||
|
||||
class RemoveComposableIntention : |
||||
PsiElementBaseIntentionAction(), |
||||
LowPriorityAction { |
||||
|
||||
override fun getText(): String { |
||||
return "Remove this Composable" |
||||
} |
||||
|
||||
override fun getFamilyName(): String { |
||||
return "Compose Multiplatform intentions" |
||||
} |
||||
|
||||
private val composableFunctionFinder: ComposableFunctionFinder = ComposableFunctionFinderImpl() |
||||
|
||||
private val getRootElement = GetRootPsiElement() |
||||
|
||||
override fun isAvailable(project: Project, editor: Editor?, element: PsiElement): Boolean { |
||||
return element.isIntentionAvailable(composableFunctionFinder) |
||||
} |
||||
|
||||
override fun invoke(project: Project, editor: Editor?, element: PsiElement) { |
||||
getRootElement(element.parent)?.delete() |
||||
} |
||||
} |
@ -0,0 +1,48 @@
|
||||
package org.jetbrains.compose.intentions |
||||
|
||||
import com.intellij.codeInsight.intention.PriorityAction |
||||
import com.intellij.codeInsight.intention.PsiElementBaseIntentionAction |
||||
import com.intellij.openapi.editor.Editor |
||||
import com.intellij.openapi.project.Project |
||||
import com.intellij.openapi.util.Iconable |
||||
import com.intellij.psi.PsiElement |
||||
import javax.swing.Icon |
||||
import org.jetbrains.compose.desktop.ide.preview.PreviewIcons |
||||
import org.jetbrains.compose.intentions.utils.composableFinder.ChildComposableFinder |
||||
import org.jetbrains.compose.intentions.utils.composableFinder.ComposableFunctionFinder |
||||
import org.jetbrains.compose.intentions.utils.getRootPsiElement.GetRootPsiElement |
||||
import org.jetbrains.compose.intentions.utils.isIntentionAvailable |
||||
import org.jetbrains.kotlin.psi.KtCallExpression |
||||
|
||||
class RemoveParentComposableIntention : |
||||
PsiElementBaseIntentionAction(), |
||||
PriorityAction { |
||||
|
||||
override fun getText(): String { |
||||
return "Remove the parent Composable" |
||||
} |
||||
|
||||
override fun getFamilyName(): String { |
||||
return "Compose Multiplatform intentions" |
||||
} |
||||
|
||||
private val getRootElement = GetRootPsiElement() |
||||
|
||||
private val composableFunctionFinder: ComposableFunctionFinder = ChildComposableFinder() |
||||
|
||||
override fun isAvailable(project: Project, editor: Editor?, element: PsiElement): Boolean { |
||||
return element.isIntentionAvailable(composableFunctionFinder) |
||||
} |
||||
|
||||
override fun invoke(project: Project, editor: Editor?, element: PsiElement) { |
||||
val callExpression = getRootElement(element.parent) as? KtCallExpression ?: return |
||||
val lambdaBlock = |
||||
callExpression.lambdaArguments.firstOrNull()?.getLambdaExpression()?.functionLiteral?.bodyExpression |
||||
?: return |
||||
callExpression.replace(lambdaBlock) |
||||
} |
||||
|
||||
override fun getPriority(): PriorityAction.Priority { |
||||
return PriorityAction.Priority.NORMAL |
||||
} |
||||
} |
@ -0,0 +1,67 @@
|
||||
package org.jetbrains.compose.intentions |
||||
|
||||
import com.intellij.codeInsight.intention.impl.IntentionActionGroup |
||||
import com.intellij.openapi.editor.Editor |
||||
import com.intellij.openapi.project.Project |
||||
import com.intellij.openapi.ui.popup.ListPopup |
||||
import com.intellij.openapi.ui.popup.PopupStep |
||||
import com.intellij.openapi.ui.popup.util.BaseListPopupStep |
||||
import com.intellij.psi.PsiFile |
||||
import com.intellij.ui.popup.list.ListPopupImpl |
||||
import org.jetbrains.compose.intentions.wrapActions.BaseWrapWithComposableAction |
||||
import org.jetbrains.compose.intentions.wrapActions.WrapWithBoxIntention |
||||
import org.jetbrains.compose.intentions.wrapActions.WrapWithCardIntention |
||||
import org.jetbrains.compose.intentions.wrapActions.WrapWithColumnIntention |
||||
import org.jetbrains.compose.intentions.wrapActions.WrapWithLzyColumnIntention |
||||
import org.jetbrains.compose.intentions.wrapActions.WrapWithLzyRowIntention |
||||
import org.jetbrains.compose.intentions.wrapActions.WrapWithRowIntention |
||||
|
||||
class WrapWithComposableIntentionGroup : |
||||
IntentionActionGroup<BaseWrapWithComposableAction>( |
||||
listOf( |
||||
WrapWithBoxIntention(), |
||||
WrapWithCardIntention(), |
||||
WrapWithColumnIntention(), |
||||
WrapWithRowIntention(), |
||||
WrapWithLzyColumnIntention(), |
||||
WrapWithLzyRowIntention() |
||||
) |
||||
) { |
||||
|
||||
private fun createPopup( |
||||
project: Project, |
||||
actions: List<BaseWrapWithComposableAction>, |
||||
invokeAction: (BaseWrapWithComposableAction) -> Unit |
||||
): ListPopup { |
||||
|
||||
val step = object : BaseListPopupStep<BaseWrapWithComposableAction>(null, actions) { |
||||
|
||||
override fun getTextFor(action: BaseWrapWithComposableAction) = action.text |
||||
|
||||
override fun onChosen(selectedValue: BaseWrapWithComposableAction, finalChoice: Boolean): PopupStep<*>? { |
||||
invokeAction(selectedValue) |
||||
return FINAL_CHOICE |
||||
} |
||||
} |
||||
|
||||
return ListPopupImpl(project, step) |
||||
} |
||||
|
||||
override fun getFamilyName(): String { |
||||
return "Compose Multiplatform intentions" |
||||
} |
||||
|
||||
override fun chooseAction( |
||||
project: Project, |
||||
editor: Editor, |
||||
file: PsiFile, |
||||
actions: List<BaseWrapWithComposableAction>, |
||||
invokeAction: (BaseWrapWithComposableAction) -> Unit |
||||
) { |
||||
createPopup(project, actions, invokeAction).showInBestPositionFor(editor) |
||||
} |
||||
|
||||
override fun getGroupText(actions: List<BaseWrapWithComposableAction>): String { |
||||
return "Wrap with Composable" |
||||
} |
||||
} |
@ -0,0 +1,36 @@
|
||||
package org.jetbrains.compose.intentions.utils |
||||
|
||||
import com.intellij.psi.PsiElement |
||||
import org.jetbrains.compose.desktop.ide.preview.isComposableFunction |
||||
import org.jetbrains.compose.intentions.utils.composableFinder.ComposableFunctionFinder |
||||
import org.jetbrains.kotlin.idea.KotlinLanguage |
||||
import org.jetbrains.kotlin.nj2k.postProcessing.resolve |
||||
import org.jetbrains.kotlin.psi.KtCallExpression |
||||
import org.jetbrains.kotlin.psi.KtNameReferenceExpression |
||||
import org.jetbrains.kotlin.psi.KtNamedFunction |
||||
import org.jetbrains.kotlin.psi.psiUtil.getChildOfType |
||||
|
||||
internal fun KtCallExpression.isComposable(): Boolean { |
||||
return getChildOfType<KtNameReferenceExpression>()?.isComposable() ?: false |
||||
} |
||||
|
||||
internal fun KtNameReferenceExpression.isComposable(): Boolean { |
||||
val ktNamedFunction = resolve() as? KtNamedFunction ?: return false |
||||
return ktNamedFunction.isComposableFunction() |
||||
} |
||||
|
||||
internal fun PsiElement.isIntentionAvailable( |
||||
composableFunctionFinder: ComposableFunctionFinder |
||||
): Boolean { |
||||
if (language != KotlinLanguage.INSTANCE) { |
||||
return false |
||||
} |
||||
|
||||
if (!isWritable) { |
||||
return false |
||||
} |
||||
|
||||
return parent?.let { parentPsiElement -> |
||||
composableFunctionFinder.isFunctionComposable(parentPsiElement) |
||||
} ?: false |
||||
} |
@ -0,0 +1,33 @@
|
||||
package org.jetbrains.compose.intentions.utils.composableFinder |
||||
|
||||
import com.intellij.psi.PsiElement |
||||
import org.jetbrains.compose.intentions.utils.isComposable |
||||
import org.jetbrains.kotlin.psi.KtCallExpression |
||||
import org.jetbrains.kotlin.psi.KtLambdaArgument |
||||
import org.jetbrains.kotlin.psi.psiUtil.getChildOfType |
||||
|
||||
class ChildComposableFinder : ComposableFunctionFinder { |
||||
|
||||
override fun isFunctionComposable(psiElement: PsiElement): Boolean { |
||||
|
||||
if (psiElement is KtCallExpression) { |
||||
psiElement.getChildOfType<KtLambdaArgument>()?.let { lambdaChild -> |
||||
return getComposableFromChildLambda(lambdaChild) |
||||
} |
||||
} |
||||
|
||||
if (psiElement.parent is KtCallExpression) { |
||||
psiElement.parent.getChildOfType<KtLambdaArgument>()?.let { lambdaChild -> |
||||
return getComposableFromChildLambda(lambdaChild) |
||||
} |
||||
} |
||||
|
||||
return false |
||||
} |
||||
|
||||
private fun getComposableFromChildLambda(lambdaArgument: KtLambdaArgument): Boolean { |
||||
val bodyExpression = lambdaArgument.getLambdaExpression()?.functionLiteral?.bodyExpression |
||||
val ktCallExpression = bodyExpression?.getChildOfType<KtCallExpression>() ?: return false |
||||
return ktCallExpression.isComposable() |
||||
} |
||||
} |
@ -0,0 +1,7 @@
|
||||
package org.jetbrains.compose.intentions.utils.composableFinder |
||||
|
||||
import com.intellij.psi.PsiElement |
||||
|
||||
interface ComposableFunctionFinder { |
||||
fun isFunctionComposable(psiElement: PsiElement): Boolean |
||||
} |
@ -0,0 +1,40 @@
|
||||
package org.jetbrains.compose.intentions.utils.composableFinder |
||||
|
||||
import com.intellij.psi.PsiElement |
||||
import org.jetbrains.compose.intentions.utils.isComposable |
||||
import org.jetbrains.kotlin.psi.KtCallExpression |
||||
import org.jetbrains.kotlin.psi.KtNameReferenceExpression |
||||
import org.jetbrains.kotlin.psi.KtProperty |
||||
import org.jetbrains.kotlin.psi.KtPropertyDelegate |
||||
import org.jetbrains.kotlin.psi.KtValueArgumentList |
||||
import org.jetbrains.kotlin.psi.psiUtil.getChildOfType |
||||
|
||||
class ComposableFunctionFinderImpl : ComposableFunctionFinder { |
||||
|
||||
override fun isFunctionComposable(psiElement: PsiElement): Boolean { |
||||
return when (psiElement) { |
||||
is KtNameReferenceExpression -> psiElement.isComposable() |
||||
is KtProperty -> detectComposableFromKtProperty(psiElement) |
||||
is KtValueArgumentList -> { |
||||
val parent = psiElement.parent as? KtCallExpression ?: return false |
||||
parent.isComposable() |
||||
} |
||||
else -> false |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* To handle both property and property delegates |
||||
*/ |
||||
private fun detectComposableFromKtProperty(psiElement: KtProperty): Boolean { |
||||
psiElement.getChildOfType<KtCallExpression>().let { propertyChildExpression -> |
||||
return if (propertyChildExpression == null) { |
||||
val propertyDelegate = psiElement.getChildOfType<KtPropertyDelegate>() ?: return false |
||||
val ktCallExpression = propertyDelegate.getChildOfType<KtCallExpression>() ?: return false |
||||
ktCallExpression.isComposable() |
||||
} else { |
||||
propertyChildExpression.isComposable() |
||||
} |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,40 @@
|
||||
package org.jetbrains.compose.intentions.utils.getRootPsiElement |
||||
|
||||
import com.intellij.psi.PsiElement |
||||
import org.jetbrains.kotlin.psi.KtCallExpression |
||||
import org.jetbrains.kotlin.psi.KtDotQualifiedExpression |
||||
import org.jetbrains.kotlin.psi.KtNameReferenceExpression |
||||
import org.jetbrains.kotlin.psi.KtProperty |
||||
import org.jetbrains.kotlin.psi.KtPropertyDelegate |
||||
import org.jetbrains.kotlin.psi.KtValueArgumentList |
||||
|
||||
/** |
||||
* To get the root element of a selected Psi element |
||||
*/ |
||||
class GetRootPsiElement { |
||||
|
||||
/** |
||||
* @param element can be |
||||
* 1. KtCallExpression, KtNameReferenceExpression - Box() |
||||
* 2. KtDotQualifiedExpression - repeatingAnimation.animateFloat |
||||
* 3. KtProperty - val systemUiController = rememberSystemUiController() |
||||
* 4. KtValueArgumentList - () |
||||
*/ |
||||
tailrec operator fun invoke(element: PsiElement): PsiElement? { |
||||
return when (element) { |
||||
is KtProperty -> element |
||||
is KtNameReferenceExpression, |
||||
is KtValueArgumentList -> invoke(element.parent) |
||||
is KtDotQualifiedExpression, |
||||
is KtCallExpression -> { |
||||
when (element.parent) { |
||||
is KtProperty, |
||||
is KtDotQualifiedExpression -> invoke(element.parent) // composable dot expression |
||||
is KtPropertyDelegate -> invoke(element.parent.parent) // composable dot expression |
||||
else -> element |
||||
} |
||||
} |
||||
else -> null |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,53 @@
|
||||
package org.jetbrains.compose.intentions.wrapActions |
||||
|
||||
import com.intellij.codeInsight.intention.HighPriorityAction |
||||
import com.intellij.codeInsight.intention.PsiElementBaseIntentionAction |
||||
import com.intellij.codeInsight.template.impl.InvokeTemplateAction |
||||
import com.intellij.codeInsight.template.impl.TemplateImpl |
||||
import com.intellij.openapi.editor.Editor |
||||
import com.intellij.openapi.project.Project |
||||
import com.intellij.psi.PsiElement |
||||
import org.jetbrains.compose.intentions.utils.composableFinder.ComposableFunctionFinder |
||||
import org.jetbrains.compose.intentions.utils.composableFinder.ComposableFunctionFinderImpl |
||||
import org.jetbrains.compose.intentions.utils.getRootPsiElement.GetRootPsiElement |
||||
import org.jetbrains.compose.intentions.utils.isIntentionAvailable |
||||
|
||||
abstract class BaseWrapWithComposableAction : |
||||
PsiElementBaseIntentionAction(), |
||||
HighPriorityAction { |
||||
|
||||
private val composableFunctionFinder: ComposableFunctionFinder by lazy { |
||||
ComposableFunctionFinderImpl() |
||||
} |
||||
|
||||
private val getRootElement by lazy { |
||||
GetRootPsiElement() |
||||
} |
||||
|
||||
override fun getFamilyName(): String { |
||||
return "Compose Multiplatform intentions" |
||||
} |
||||
|
||||
override fun isAvailable(project: Project, editor: Editor?, element: PsiElement): Boolean { |
||||
return element.isIntentionAvailable(composableFunctionFinder) |
||||
} |
||||
|
||||
override fun startInWriteAction(): Boolean = true |
||||
|
||||
override fun invoke(project: Project, editor: Editor?, element: PsiElement) { |
||||
getRootElement(element.parent)?.let { rootElement -> |
||||
val selectionModel = editor!!.selectionModel |
||||
val textRange = rootElement.textRange |
||||
selectionModel.setSelection(textRange.startOffset, textRange.endOffset) |
||||
|
||||
InvokeTemplateAction( |
||||
getTemplate(), |
||||
editor, |
||||
project, |
||||
HashSet() |
||||
).perform() |
||||
} |
||||
} |
||||
|
||||
protected abstract fun getTemplate(): TemplateImpl? |
||||
} |
@ -0,0 +1,70 @@
|
||||
package org.jetbrains.compose.intentions.wrapActions |
||||
|
||||
import com.intellij.codeInsight.template.impl.TemplateImpl |
||||
import com.intellij.codeInsight.template.impl.TemplateSettings |
||||
|
||||
class WrapWithBoxIntention : BaseWrapWithComposableAction() { |
||||
|
||||
override fun getText(): String { |
||||
return "Wrap with Box" |
||||
} |
||||
|
||||
override fun getTemplate(): TemplateImpl? { |
||||
return TemplateSettings.getInstance().getTemplate("boxcomp", "ComposeMultiplatformTemplates") |
||||
} |
||||
} |
||||
|
||||
class WrapWithCardIntention : BaseWrapWithComposableAction() { |
||||
|
||||
override fun getText(): String { |
||||
return "Wrap with Card" |
||||
} |
||||
|
||||
override fun getTemplate(): TemplateImpl? { |
||||
return TemplateSettings.getInstance().getTemplate("cardcomp", "ComposeMultiplatformTemplates") |
||||
} |
||||
} |
||||
|
||||
class WrapWithColumnIntention : BaseWrapWithComposableAction() { |
||||
|
||||
override fun getText(): String { |
||||
return "Wrap with Column" |
||||
} |
||||
|
||||
override fun getTemplate(): TemplateImpl? { |
||||
return TemplateSettings.getInstance().getTemplate("columncomp", "ComposeMultiplatformTemplates") |
||||
} |
||||
} |
||||
|
||||
class WrapWithRowIntention : BaseWrapWithComposableAction() { |
||||
|
||||
override fun getText(): String { |
||||
return "Wrap with Row" |
||||
} |
||||
|
||||
override fun getTemplate(): TemplateImpl? { |
||||
return TemplateSettings.getInstance().getTemplate("rowcomp", "ComposeMultiplatformTemplates") |
||||
} |
||||
} |
||||
|
||||
class WrapWithLzyColumnIntention : BaseWrapWithComposableAction() { |
||||
|
||||
override fun getText(): String { |
||||
return "Wrap with LazyColumn" |
||||
} |
||||
|
||||
override fun getTemplate(): TemplateImpl? { |
||||
return TemplateSettings.getInstance().getTemplate("lazycolumncomp", "ComposeMultiplatformTemplates") |
||||
} |
||||
} |
||||
|
||||
class WrapWithLzyRowIntention : BaseWrapWithComposableAction() { |
||||
|
||||
override fun getText(): String { |
||||
return "Wrap with LazyRow" |
||||
} |
||||
|
||||
override fun getTemplate(): TemplateImpl? { |
||||
return TemplateSettings.getInstance().getTemplate("lazyrowcomp", "ComposeMultiplatformTemplates") |
||||
} |
||||
} |
@ -0,0 +1,4 @@
|
||||
@Composable |
||||
fun Column() { |
||||
|
||||
} |
@ -0,0 +1,4 @@
|
||||
@Composable |
||||
fun Column() { |
||||
Text("Abc") |
||||
} |
@ -0,0 +1,7 @@
|
||||
<html lang="en"> |
||||
<body> |
||||
<p> |
||||
A simple intention to remove a Composable altogether. |
||||
</p> |
||||
</body> |
||||
</html> |
@ -0,0 +1,4 @@
|
||||
@Composable |
||||
fun Column() { |
||||
Text("Abc") |
||||
} |
@ -0,0 +1,6 @@
|
||||
@Composable |
||||
fun Column() { |
||||
Button(){ |
||||
Text("Abc") |
||||
} |
||||
} |
@ -0,0 +1,7 @@
|
||||
<html lang="en"> |
||||
<body> |
||||
<p> |
||||
An intention to remove a parent Composable, and unwrap its children. |
||||
</p> |
||||
</body> |
||||
</html> |
@ -0,0 +1,15 @@
|
||||
<html lang="en"> |
||||
<body> |
||||
<p> |
||||
A simple intention to wrap your <a href="https://developer.android.com/jetpack/compose/">Composables</a> with another |
||||
Composable. Just keep your caret in the Editor on the composable, and press on the yellow bulb on the left, or press |
||||
Alt+Enter to show hints/intentions. You can choose to - |
||||
1. Wrap with Box |
||||
2. Wrap with Card |
||||
3. Wrap with Column |
||||
4. Wrap with Row |
||||
5. Wrap with LazyColumn |
||||
6. Wrap with LazyRow |
||||
</p> |
||||
</body> |
||||
</html> |
@ -0,0 +1,71 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?> |
||||
<templateSet group="ComposeMultiplatformTemplates"> |
||||
|
||||
<template name="boxcomp" description="Insert Box Composable" |
||||
value="$COMPOSABLE$ { $SELECTION$ }" |
||||
toReformat="true" toShortenFQNames="true"> |
||||
<variable name="COMPOSABLE" expression="" defaultValue=""androidx.compose.foundation.layout.Box"" |
||||
alwaysStopAt="true"/> |
||||
<context> |
||||
<option name="KOTLIN" value="true"/> |
||||
<option name="KOTLIN_COMMENT" value="false"/> |
||||
</context> |
||||
</template> |
||||
|
||||
|
||||
<template name="cardcomp" description="Insert Card Composable" |
||||
value="$COMPOSABLE$ { $SELECTION$ }" |
||||
toReformat="true" toShortenFQNames="true"> |
||||
<variable name="COMPOSABLE" expression="" defaultValue=""androidx.compose.material.Card"" |
||||
alwaysStopAt="true"/> |
||||
<context> |
||||
<option name="KOTLIN" value="true"/> |
||||
<option name="KOTLIN_COMMENT" value="false"/> |
||||
</context> |
||||
</template> |
||||
|
||||
<template name="columncomp" description="Insert Column Composable" |
||||
value="$COMPOSABLE$ { $SELECTION$ }" |
||||
toReformat="true" toShortenFQNames="true"> |
||||
<variable name="COMPOSABLE" expression="" defaultValue=""androidx.compose.foundation.layout.Column"" |
||||
alwaysStopAt="true"/> |
||||
<context> |
||||
<option name="KOTLIN" value="true"/> |
||||
<option name="KOTLIN_COMMENT" value="false"/> |
||||
</context> |
||||
</template> |
||||
|
||||
<template name="rowcomp" description="Insert Row Composable" |
||||
value="$COMPOSABLE$ { $SELECTION$ }" |
||||
toReformat="true" toShortenFQNames="true"> |
||||
<variable name="COMPOSABLE" expression="" defaultValue=""androidx.compose.foundation.layout.Row"" |
||||
alwaysStopAt="true"/> |
||||
<context> |
||||
<option name="KOTLIN" value="true"/> |
||||
<option name="KOTLIN_COMMENT" value="false"/> |
||||
</context> |
||||
</template> |
||||
|
||||
<template name="lazycolumncomp" description="Insert Lazy Column Composable" |
||||
value="$COMPOSABLE$ { item { $SELECTION$ } }" |
||||
toReformat="true" toShortenFQNames="true"> |
||||
<variable name="COMPOSABLE" expression="" defaultValue=""androidx.compose.foundation.lazy.LazyColumn"" |
||||
alwaysStopAt="true"/> |
||||
<context> |
||||
<option name="KOTLIN" value="true"/> |
||||
<option name="KOTLIN_COMMENT" value="false"/> |
||||
</context> |
||||
</template> |
||||
|
||||
<template name="lazyrowcomp" description="Insert Lazy Row Composable" |
||||
value="$COMPOSABLE$ { item { $SELECTION$ } }" |
||||
toReformat="true" toShortenFQNames="true"> |
||||
<variable name="COMPOSABLE" expression="" defaultValue=""androidx.compose.foundation.lazy.LazyRow"" |
||||
alwaysStopAt="true"/> |
||||
<context> |
||||
<option name="KOTLIN" value="true"/> |
||||
<option name="KOTLIN_COMMENT" value="false"/> |
||||
</context> |
||||
</template> |
||||
|
||||
</templateSet> |
@ -0,0 +1,307 @@
|
||||
package org.jetbrains.compose.intentions.utils.getRootPsiElement |
||||
|
||||
import com.intellij.testFramework.fixtures.LightJavaCodeInsightFixtureTestCase |
||||
import junit.framework.TestCase |
||||
import org.intellij.lang.annotations.Language |
||||
import org.jetbrains.kotlin.psi.KtCallExpression |
||||
import org.jetbrains.kotlin.psi.KtDotQualifiedExpression |
||||
import org.jetbrains.kotlin.psi.KtNameReferenceExpression |
||||
import org.jetbrains.kotlin.psi.KtNamedFunction |
||||
import org.jetbrains.kotlin.psi.KtProperty |
||||
import org.jetbrains.kotlin.psi.KtPsiFactory |
||||
import org.jetbrains.kotlin.psi.KtValueArgumentList |
||||
import org.junit.Test |
||||
import org.junit.runner.RunWith |
||||
import org.junit.runners.JUnit4 |
||||
|
||||
@RunWith(JUnit4::class) |
||||
class GetRootPsiElementTest : LightJavaCodeInsightFixtureTestCase() { |
||||
|
||||
private val getRootElement = GetRootPsiElement() |
||||
|
||||
@Test |
||||
fun `when a name reference expression is selected , but root is a property , the property should be returned`() { |
||||
val ktPsiFactory = KtPsiFactory(project) |
||||
|
||||
@Language("Kotlin") |
||||
val template = """ |
||||
val systemUiController = rememberSystemUiController() |
||||
""".trimIndent() |
||||
|
||||
val file = ktPsiFactory.createFile(template) |
||||
|
||||
val property = file.lastChild as KtProperty |
||||
|
||||
val ktNameReferenceExpression = (property.lastChild as KtCallExpression).firstChild as KtNameReferenceExpression |
||||
|
||||
TestCase.assertEquals("rememberSystemUiController", ktNameReferenceExpression.text) |
||||
|
||||
TestCase.assertEquals(property, getRootElement.invoke(ktNameReferenceExpression)) |
||||
} |
||||
|
||||
@Test |
||||
fun `when a name reference expression is selected, with a call expression as root, call expression should be returned`() { |
||||
val ktPsiFactory = KtPsiFactory(project) |
||||
|
||||
@Language("Kotlin") |
||||
val template = """ |
||||
fun Box(block:()->Unit) { |
||||
|
||||
} |
||||
|
||||
fun OuterComposable() { |
||||
Box() { |
||||
|
||||
} |
||||
} |
||||
""".trimIndent().trim() |
||||
|
||||
val file = ktPsiFactory.createFile(template) |
||||
|
||||
val ktNamedFunction = file.lastChild as KtNamedFunction |
||||
|
||||
val callExpression = ktNamedFunction.lastChild.children.find { it is KtCallExpression }!! |
||||
val ktNameReferenceExpression = callExpression.firstChild as KtNameReferenceExpression |
||||
|
||||
TestCase.assertEquals("Box", ktNameReferenceExpression.text) |
||||
|
||||
TestCase.assertEquals( |
||||
callExpression, |
||||
getRootElement.invoke(ktNameReferenceExpression) |
||||
) |
||||
} |
||||
|
||||
@Test |
||||
fun `when an argument list element is selected, with a call expression as root, call expression should be returned`() { |
||||
val ktPsiFactory = KtPsiFactory(project) |
||||
|
||||
@Language("Kotlin") |
||||
val template = """ |
||||
@Composable |
||||
fun Box(block:()->Unit) { |
||||
|
||||
} |
||||
|
||||
fun OuterComposable() { |
||||
// Argument List Element |
||||
// | |
||||
// v |
||||
Box() { |
||||
|
||||
} |
||||
} |
||||
""".trimIndent().trim() |
||||
|
||||
val file = ktPsiFactory.createFile(template) |
||||
|
||||
val ktNamedFunction = file.lastChild as KtNamedFunction |
||||
|
||||
val callExpression = ktNamedFunction.lastChild.children.find { it is KtCallExpression }!! |
||||
|
||||
val argumentListElement = callExpression.firstChild.nextSibling as KtValueArgumentList |
||||
|
||||
TestCase.assertEquals("()", argumentListElement.text) |
||||
|
||||
TestCase.assertEquals( |
||||
callExpression, |
||||
getRootElement.invoke(argumentListElement) |
||||
) |
||||
} |
||||
|
||||
@Test |
||||
fun `when a name reference expression is selected, with a delegated property as root, property should be returned`() { |
||||
val ktPsiFactory = KtPsiFactory(project) |
||||
|
||||
@Language("Kotlin") |
||||
val template = """ |
||||
var isComposable by remember { |
||||
true |
||||
} |
||||
""".trimIndent().trim() |
||||
|
||||
val file = ktPsiFactory.createFile(template) |
||||
|
||||
val property = file.lastChild as KtProperty |
||||
|
||||
val referenceExpression = property.lastChild.lastChild.firstChild as KtNameReferenceExpression |
||||
|
||||
TestCase.assertEquals("remember", referenceExpression.text) |
||||
|
||||
TestCase.assertEquals( |
||||
property, |
||||
getRootElement.invoke(referenceExpression) |
||||
) |
||||
} |
||||
|
||||
@Test |
||||
fun `when a name reference expression with dot reference expression is selected, with a delegated property as root, property should be returned`() { |
||||
val ktPsiFactory = KtPsiFactory(project) |
||||
|
||||
@Language("Kotlin") |
||||
val template = """ |
||||
val repeatingAnimation = rememberInfiniteTransition() |
||||
|
||||
val offset by repeatingAnimation.animateFloat( |
||||
0f, |
||||
-20f, |
||||
infiniteRepeatable( |
||||
repeatMode = RepeatMode.Reverse, |
||||
animation = tween( |
||||
durationMillis = 1000, |
||||
easing = LinearEasing |
||||
) |
||||
) |
||||
) |
||||
""".trimIndent().trim() |
||||
|
||||
val file = ktPsiFactory.createFile(template) |
||||
|
||||
val property = file.lastChild as KtProperty |
||||
|
||||
val dotQualifiedExpression = property.lastChild.lastChild as KtDotQualifiedExpression |
||||
|
||||
val referenceExpression = dotQualifiedExpression.lastChild.firstChild as KtNameReferenceExpression |
||||
|
||||
TestCase.assertEquals("animateFloat", referenceExpression.text) |
||||
|
||||
TestCase.assertEquals( |
||||
property, |
||||
getRootElement.invoke(referenceExpression) |
||||
) |
||||
} |
||||
|
||||
@Test |
||||
fun `when a name reference expression with dot reference expression is selected, with a property and dot qualified expression as root, property should be returned`() { |
||||
val ktPsiFactory = KtPsiFactory(project) |
||||
|
||||
@Language("Kotlin") |
||||
val template = """ |
||||
val repeatingAnimation = rememberInfiniteTransition() |
||||
|
||||
val offset = repeatingAnimation.animateFloat( |
||||
0f, |
||||
-20f, |
||||
infiniteRepeatable( |
||||
repeatMode = RepeatMode.Reverse, |
||||
animation = tween( |
||||
durationMillis = 1000, |
||||
easing = LinearEasing |
||||
) |
||||
) |
||||
) |
||||
""".trimIndent().trim() |
||||
|
||||
val file = ktPsiFactory.createFile(template) |
||||
|
||||
val property = file.lastChild as KtProperty |
||||
|
||||
val dotQualifiedExpression = property.lastChild as KtDotQualifiedExpression |
||||
|
||||
val referenceExpression = dotQualifiedExpression.lastChild.firstChild as KtNameReferenceExpression |
||||
|
||||
TestCase.assertEquals("animateFloat", referenceExpression.text) |
||||
|
||||
TestCase.assertEquals( |
||||
property, |
||||
getRootElement.invoke(referenceExpression) |
||||
) |
||||
} |
||||
|
||||
@Test |
||||
fun `when a dot qualified expression is selected, with a delegated property as root, property should be returned`() { |
||||
val ktPsiFactory = KtPsiFactory(project) |
||||
|
||||
@Language("Kotlin") |
||||
val template = """ |
||||
val repeatingAnimation = rememberInfiniteTransition() |
||||
|
||||
val offset by repeatingAnimation.animateFloat( |
||||
0f, |
||||
-20f, |
||||
infiniteRepeatable( |
||||
repeatMode = RepeatMode.Reverse, |
||||
animation = tween( |
||||
durationMillis = 1000, |
||||
easing = LinearEasing |
||||
) |
||||
) |
||||
) |
||||
""".trimIndent().trim() |
||||
|
||||
val file = ktPsiFactory.createFile(template) |
||||
|
||||
val property = file.lastChild as KtProperty |
||||
|
||||
val dotQualifiedExpression = property.lastChild.lastChild as KtDotQualifiedExpression |
||||
|
||||
TestCase.assertEquals( |
||||
""" |
||||
repeatingAnimation.animateFloat( |
||||
0f, |
||||
-20f, |
||||
infiniteRepeatable( |
||||
repeatMode = RepeatMode.Reverse, |
||||
animation = tween( |
||||
durationMillis = 1000, |
||||
easing = LinearEasing |
||||
) |
||||
) |
||||
) |
||||
""".trimIndent().trim(), dotQualifiedExpression.text |
||||
) |
||||
|
||||
TestCase.assertEquals( |
||||
property, |
||||
getRootElement.invoke(dotQualifiedExpression) |
||||
) |
||||
} |
||||
|
||||
@Test |
||||
fun `when a dot qualified expression is selected, with a property as root, property should be returned`() { |
||||
val ktPsiFactory = KtPsiFactory(project) |
||||
|
||||
@Language("Kotlin") |
||||
val template = """ |
||||
val repeatingAnimation = rememberInfiniteTransition() |
||||
|
||||
val offset = repeatingAnimation.animateFloat( |
||||
0f, |
||||
-20f, |
||||
infiniteRepeatable( |
||||
repeatMode = RepeatMode.Reverse, |
||||
animation = tween( |
||||
durationMillis = 1000, |
||||
easing = LinearEasing |
||||
) |
||||
) |
||||
) |
||||
""".trimIndent().trim() |
||||
|
||||
val file = ktPsiFactory.createFile(template) |
||||
|
||||
val property = file.lastChild as KtProperty |
||||
|
||||
val dotQualifiedExpression = property.lastChild as KtDotQualifiedExpression |
||||
|
||||
TestCase.assertEquals( |
||||
""" |
||||
repeatingAnimation.animateFloat( |
||||
0f, |
||||
-20f, |
||||
infiniteRepeatable( |
||||
repeatMode = RepeatMode.Reverse, |
||||
animation = tween( |
||||
durationMillis = 1000, |
||||
easing = LinearEasing |
||||
) |
||||
) |
||||
) |
||||
""".trimIndent().trim(), dotQualifiedExpression.text |
||||
) |
||||
|
||||
TestCase.assertEquals( |
||||
property, |
||||
getRootElement.invoke(dotQualifiedExpression) |
||||
) |
||||
} |
||||
} |
Loading…
Reference in new issue