thelumiereguy
3 years ago
21 changed files with 812 additions and 0 deletions
@ -0,0 +1,40 @@
|
||||
package org.jetbrains.compose.intentions.remove_composable |
||||
|
||||
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.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.is_intention_available.IsIntentionAvailable |
||||
import org.jetbrains.compose.intentions.utils.composable_finder.ComposableFunctionFinder |
||||
import org.jetbrains.compose.intentions.utils.composable_finder.DeepComposableFunctionFinder |
||||
import org.jetbrains.compose.intentions.utils.get_root_element.GetRootElement |
||||
|
||||
class RemoveComposableIntention : PsiElementBaseIntentionAction(), Iconable, LowPriorityAction, IsIntentionAvailable { |
||||
|
||||
override fun getText(): String { |
||||
return "Remove this Composable" |
||||
} |
||||
|
||||
override fun getFamilyName(): String { |
||||
return "Compose Multiplatform intentions" |
||||
} |
||||
|
||||
private val composableFunctionFinder: ComposableFunctionFinder = DeepComposableFunctionFinder() |
||||
|
||||
private val getRootElement = GetRootElement() |
||||
|
||||
override fun isAvailable(project: Project, editor: Editor?, element: PsiElement): Boolean { |
||||
return element.isAvailable(composableFunctionFinder) |
||||
} |
||||
|
||||
override fun invoke(project: Project, editor: Editor?, element: PsiElement) { |
||||
getRootElement(element.parent)?.delete() |
||||
} |
||||
|
||||
override fun getIcon(flags: Int): Icon = PreviewIcons.COMPOSE |
||||
|
||||
} |
@ -0,0 +1,56 @@
|
||||
package org.jetbrains.compose.intentions.remove_parent_composable |
||||
|
||||
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 com.intellij.psi.util.parentOfType |
||||
import javax.swing.Icon |
||||
import org.jetbrains.compose.desktop.ide.preview.PreviewIcons |
||||
import org.jetbrains.compose.intentions.utils.is_intention_available.IsIntentionAvailable |
||||
import org.jetbrains.compose.intentions.utils.composable_finder.ComposableFunctionFinder |
||||
import org.jetbrains.compose.intentions.utils.composable_finder.NestedComposableFinder |
||||
import org.jetbrains.kotlin.psi.KtCallExpression |
||||
import org.jetbrains.kotlin.psi.KtNameReferenceExpression |
||||
import org.jetbrains.kotlin.psi.KtValueArgumentList |
||||
|
||||
class RemoveParentComposableIntention : PsiElementBaseIntentionAction(), |
||||
Iconable, |
||||
PriorityAction, |
||||
IsIntentionAvailable { |
||||
|
||||
override fun getText(): String { |
||||
return "Remove the parent Composable" |
||||
} |
||||
|
||||
override fun getFamilyName(): String { |
||||
return "Compose Multiplatform intentions" |
||||
} |
||||
|
||||
private val composableFunctionFinder: ComposableFunctionFinder = NestedComposableFinder() |
||||
|
||||
override fun isAvailable(project: Project, editor: Editor?, element: PsiElement): Boolean { |
||||
return element.isAvailable(composableFunctionFinder) |
||||
} |
||||
|
||||
override fun invoke(project: Project, editor: Editor?, element: PsiElement) { |
||||
val wrapper = if (element.parent is KtValueArgumentList) { |
||||
element.parent.prevSibling as? KtNameReferenceExpression ?: return |
||||
} else { |
||||
element.parentOfType() ?: return |
||||
} |
||||
val callExpression = (wrapper.parent as? KtCallExpression) ?: return |
||||
val lambdaBlock = |
||||
callExpression.lambdaArguments.firstOrNull()?.getLambdaExpression()?.functionLiteral?.bodyExpression |
||||
?: return |
||||
callExpression.replace(lambdaBlock) |
||||
} |
||||
|
||||
override fun getIcon(flags: Int): Icon = PreviewIcons.COMPOSE |
||||
|
||||
override fun getPriority(): PriorityAction.Priority { |
||||
return PriorityAction.Priority.NORMAL |
||||
} |
||||
} |
@ -0,0 +1,8 @@
|
||||
package org.jetbrains.compose.intentions.utils.composable_finder |
||||
|
||||
import com.intellij.psi.PsiElement |
||||
|
||||
interface ComposableFunctionFinder { |
||||
|
||||
fun isFunctionComposable(psiElement: PsiElement): Boolean |
||||
} |
@ -0,0 +1,42 @@
|
||||
package org.jetbrains.compose.intentions.utils.composable_finder |
||||
|
||||
import com.intellij.psi.PsiElement |
||||
import org.jetbrains.compose.intentions.utils.is_psi_element_composable.IsPsiElementComposable |
||||
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 DeepComposableFunctionFinder : ComposableFunctionFinder, IsPsiElementComposable { |
||||
|
||||
override fun isFunctionComposable(psiElement: PsiElement): Boolean { |
||||
return when (psiElement) { |
||||
is KtNameReferenceExpression -> psiElement.isComposable() |
||||
is KtCallExpression -> 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,34 @@
|
||||
package org.jetbrains.compose.intentions.utils.composable_finder |
||||
|
||||
import com.intellij.psi.PsiElement |
||||
import org.jetbrains.compose.intentions.utils.is_psi_element_composable.IsPsiElementComposable |
||||
import org.jetbrains.kotlin.psi.KtCallExpression |
||||
import org.jetbrains.kotlin.psi.KtLambdaArgument |
||||
import org.jetbrains.kotlin.psi.psiUtil.getChildOfType |
||||
|
||||
|
||||
class NestedComposableFinder : ComposableFunctionFinder, IsPsiElementComposable { |
||||
|
||||
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,39 @@
|
||||
package org.jetbrains.compose.intentions.utils.get_root_element |
||||
|
||||
import com.intellij.psi.PsiElement |
||||
import org.jetbrains.kotlin.psi.* |
||||
|
||||
/** |
||||
* KtValueArgumentList -> Parent -> KtNameReferenceExpression -> Parent -> KtCallExpression -> Parent -> KtPropertyDelegate -> Parent -> Property |
||||
* KtNameReferenceExpression -> Parent -> KtCallExpression -> Parent -> KtDotQualifiedExpression -> Parent -> KtPropertyDelegate -> Property |
||||
* KtNameReferenceExpression -> Parent -> KtCallExpression -> Parent -> KtPropertyDelegate -> Parent -> Property |
||||
* KtNameReferenceExpression -> Parent -> KtCallExpression -> Parent -> Property |
||||
* KtNameReferenceExpression -> Parent -> KtCallExpression |
||||
**/ |
||||
class GetRootElement { |
||||
|
||||
/** |
||||
* element can be CallExpression (Composable Function) or Property (Composable Property like remember) |
||||
*/ |
||||
tailrec operator fun invoke(element: PsiElement, iteration: Int = 0): PsiElement? { |
||||
if (iteration > 5) { // fail safe |
||||
return null |
||||
} |
||||
|
||||
return when (element) { |
||||
is KtProperty -> element |
||||
is KtNameReferenceExpression, |
||||
is KtValueArgumentList -> invoke(element.parent, iteration + 1) |
||||
is KtDotQualifiedExpression, |
||||
is KtCallExpression -> { |
||||
when (element.parent) { |
||||
is KtProperty, |
||||
is KtDotQualifiedExpression -> invoke(element.parent, iteration + 1) //composable dot expression |
||||
is KtPropertyDelegate -> invoke(element.parent.parent, iteration + 1) //composable dot expression |
||||
else -> element |
||||
} |
||||
} |
||||
else -> element |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,25 @@
|
||||
package org.jetbrains.compose.intentions.utils.is_intention_available |
||||
|
||||
import com.intellij.psi.PsiElement |
||||
import org.jetbrains.compose.intentions.utils.composable_finder.ComposableFunctionFinder |
||||
import org.jetbrains.kotlin.idea.KotlinLanguage |
||||
|
||||
interface IsIntentionAvailable { |
||||
|
||||
fun PsiElement.isAvailable( |
||||
composableFunctionFinder: ComposableFunctionFinder |
||||
): Boolean { |
||||
if (language != KotlinLanguage.INSTANCE) { |
||||
return false |
||||
} |
||||
|
||||
if (!isWritable) { |
||||
return false |
||||
} |
||||
|
||||
return parent?.let { parentPsiElement -> |
||||
composableFunctionFinder.isFunctionComposable(parentPsiElement) |
||||
} ?: false |
||||
} |
||||
|
||||
} |
@ -0,0 +1,21 @@
|
||||
package org.jetbrains.compose.intentions.utils.is_psi_element_composable |
||||
|
||||
import org.jetbrains.compose.desktop.ide.preview.isComposableFunction |
||||
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 |
||||
|
||||
interface IsPsiElementComposable { |
||||
|
||||
fun KtCallExpression.isComposable(): Boolean { |
||||
return getChildOfType<KtNameReferenceExpression>()?.isComposable() ?: false |
||||
} |
||||
|
||||
fun KtNameReferenceExpression.isComposable(): Boolean { |
||||
val ktNamedFunction = resolve() as? KtNamedFunction ?: return false |
||||
return ktNamedFunction.isComposableFunction() |
||||
} |
||||
|
||||
} |
@ -0,0 +1,72 @@
|
||||
package org.jetbrains.compose.intentions.wrap_with_composable |
||||
|
||||
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.openapi.util.Iconable |
||||
import com.intellij.psi.PsiFile |
||||
import com.intellij.ui.popup.list.ListPopupImpl |
||||
import javax.swing.Icon |
||||
import org.jetbrains.compose.desktop.ide.preview.PreviewIcons |
||||
import org.jetbrains.compose.intentions.wrap_with_composable.wrap_with_actions.BaseWrapWithComposableAction |
||||
import org.jetbrains.compose.intentions.wrap_with_composable.wrap_with_actions.WrapWithBoxIntention |
||||
import org.jetbrains.compose.intentions.wrap_with_composable.wrap_with_actions.WrapWithCardIntention |
||||
import org.jetbrains.compose.intentions.wrap_with_composable.wrap_with_actions.WrapWithColumnIntention |
||||
import org.jetbrains.compose.intentions.wrap_with_composable.wrap_with_actions.WrapWithLzyColumnIntention |
||||
import org.jetbrains.compose.intentions.wrap_with_composable.wrap_with_actions.WrapWithLzyRowIntention |
||||
import org.jetbrains.compose.intentions.wrap_with_composable.wrap_with_actions.WrapWithRowIntention |
||||
|
||||
class WrapWithComposableIntentionGroup : |
||||
IntentionActionGroup<BaseWrapWithComposableAction>( |
||||
listOf( |
||||
WrapWithBoxIntention(), |
||||
WrapWithCardIntention(), |
||||
WrapWithColumnIntention(), |
||||
WrapWithRowIntention(), |
||||
WrapWithLzyColumnIntention(), |
||||
WrapWithLzyRowIntention() |
||||
) |
||||
), Iconable { |
||||
|
||||
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" |
||||
} |
||||
|
||||
override fun getIcon(flags: Int): Icon = PreviewIcons.COMPOSE |
||||
} |
@ -0,0 +1,54 @@
|
||||
package org.jetbrains.compose.intentions.wrap_with_composable.wrap_with_actions |
||||
|
||||
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.is_intention_available.IsIntentionAvailable |
||||
import org.jetbrains.compose.intentions.utils.composable_finder.ComposableFunctionFinder |
||||
import org.jetbrains.compose.intentions.utils.composable_finder.DeepComposableFunctionFinder |
||||
import org.jetbrains.compose.intentions.utils.get_root_element.GetRootElement |
||||
|
||||
abstract class BaseWrapWithComposableAction : PsiElementBaseIntentionAction(), |
||||
HighPriorityAction, |
||||
IsIntentionAvailable { |
||||
|
||||
private val composableFunctionFinder: ComposableFunctionFinder by lazy { |
||||
DeepComposableFunctionFinder() |
||||
} |
||||
|
||||
private val getRootElement by lazy { |
||||
GetRootElement() |
||||
} |
||||
|
||||
override fun getFamilyName(): String { |
||||
return "Compose Multiplatform intentions" |
||||
} |
||||
|
||||
override fun isAvailable(project: Project, editor: Editor?, element: PsiElement): Boolean { |
||||
return element.isAvailable(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.wrap_with_composable.wrap_with_actions |
||||
|
||||
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("WwB", "ComposeMultiplatformTemplates") |
||||
} |
||||
} |
||||
|
||||
class WrapWithCardIntention : BaseWrapWithComposableAction() { |
||||
|
||||
override fun getText(): String { |
||||
return "Wrap with Card" |
||||
} |
||||
|
||||
override fun getTemplate(): TemplateImpl? { |
||||
return TemplateSettings.getInstance().getTemplate("WwC", "ComposeMultiplatformTemplates") |
||||
} |
||||
} |
||||
|
||||
class WrapWithColumnIntention : BaseWrapWithComposableAction() { |
||||
|
||||
override fun getText(): String { |
||||
return "Wrap with Column" |
||||
} |
||||
|
||||
override fun getTemplate(): TemplateImpl? { |
||||
return TemplateSettings.getInstance().getTemplate("WwCol", "ComposeMultiplatformTemplates") |
||||
} |
||||
} |
||||
|
||||
class WrapWithRowIntention : BaseWrapWithComposableAction() { |
||||
|
||||
override fun getText(): String { |
||||
return "Wrap with Row" |
||||
} |
||||
|
||||
override fun getTemplate(): TemplateImpl? { |
||||
return TemplateSettings.getInstance().getTemplate("WwRow", "ComposeMultiplatformTemplates") |
||||
} |
||||
} |
||||
|
||||
class WrapWithLzyColumnIntention : BaseWrapWithComposableAction() { |
||||
|
||||
override fun getText(): String { |
||||
return "Wrap with LazyColumn" |
||||
} |
||||
|
||||
override fun getTemplate(): TemplateImpl? { |
||||
return TemplateSettings.getInstance().getTemplate("WwLazyCol", "ComposeMultiplatformTemplates") |
||||
} |
||||
} |
||||
|
||||
class WrapWithLzyRowIntention : BaseWrapWithComposableAction() { |
||||
|
||||
override fun getText(): String { |
||||
return "Wrap with LazyRow" |
||||
} |
||||
|
||||
override fun getTemplate(): TemplateImpl? { |
||||
return TemplateSettings.getInstance().getTemplate("WwLazyRow", "ComposeMultiplatformTemplates") |
||||
} |
||||
} |
@ -0,0 +1,5 @@
|
||||
@Composable |
||||
fun Column() { |
||||
Text("Abc") |
||||
Text("Abc") |
||||
} |
@ -0,0 +1,7 @@
|
||||
<html lang="en"> |
||||
<body> |
||||
<p> |
||||
A simple intention to remove the composable altogether. |
||||
</p> |
||||
</body> |
||||
</html> |
@ -0,0 +1,4 @@
|
||||
@Composable |
||||
Button() { |
||||
Text("Abc") |
||||
} |
@ -0,0 +1,6 @@
|
||||
@Composable |
||||
fun Column() { |
||||
Button(){ |
||||
Text("Abc") |
||||
} |
||||
} |
@ -0,0 +1,7 @@
|
||||
<html lang="en"> |
||||
<body> |
||||
<p> |
||||
A simple intention to just remove the 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,65 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?> |
||||
<templateSet group="ComposeMultiplatformTemplates"> |
||||
|
||||
<template name="WwB" description="Wrap with Box" |
||||
value="$COMPOSABLE$(modifier = androidx.compose.ui.Modifier) { $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="WwC" description="Wrap with Card" |
||||
value="$COMPOSABLE$(modifier = androidx.compose.ui.Modifier) { $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="WwCol" description="Wrap with Column" |
||||
value="$COMPOSABLE$(modifier = androidx.compose.ui.Modifier) { $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="WwRow" description="Wrap with Row" |
||||
value="$COMPOSABLE$(modifier = androidx.compose.ui.Modifier) { $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="WwLazyCol" description="Wrap with Lazy Column" |
||||
value="$COMPOSABLE$(modifier = androidx.compose.ui.Modifier) { 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="WwLazyRow" description="Wrap with Lazy Row" |
||||
value="$COMPOSABLE$(modifier = androidx.compose.ui.Modifier) { 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,216 @@
|
||||
package org.jetbrains.compose.intentions.utils.get_root_element |
||||
|
||||
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 GetRootElementTest : LightJavaCodeInsightFixtureTestCase() { |
||||
|
||||
private val getRootElement = GetRootElement() |
||||
|
||||
@Test |
||||
fun `when a name reference expression is selected , but root is a property , the property should be returned as root element`() { |
||||
val ktPsiFactory = KtPsiFactory(project) |
||||
|
||||
@Language("Kotlin") |
||||
val template = """ |
||||
val systemUiController = rememberSystemUiController() |
||||
""".trimIndent() |
||||
.trim() |
||||
|
||||
val file = ktPsiFactory.createFile(template) |
||||
|
||||
val property = file.lastChild as KtProperty |
||||
|
||||
val ktNameReferenceExpression = (property.lastChild as KtCallExpression).firstChild as KtNameReferenceExpression |
||||
|
||||
TestCase.assertEquals(property, getRootElement(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 = """ |
||||
@Composable |
||||
fun Box(block:()->Unit) { |
||||
|
||||
} |
||||
|
||||
fun OuterComposable() { |
||||
// Call Expression - Box |
||||
// | |
||||
// v |
||||
Box() { |
||||
|
||||
} |
||||
} |
||||
""".trimIndent().trim() |
||||
|
||||
val file = ktPsiFactory.createFile(template) |
||||
|
||||
val ktNamedFunction = file.lastChild as KtNamedFunction |
||||
|
||||
val callExpression = ktNamedFunction.lastChild.children.find { it is KtCallExpression }!! |
||||
|
||||
TestCase.assertEquals( |
||||
callExpression, |
||||
getRootElement(callExpression.firstChild as 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() { |
||||
|
||||
} // Name Reference Expression |
||||
} |
||||
""".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( |
||||
callExpression, |
||||
getRootElement(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 = """ |
||||
// Delegated property |
||||
// | |
||||
// v |
||||
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( |
||||
property, |
||||
getRootElement(referenceExpression) |
||||
) |
||||
} |
||||
|
||||
@Test |
||||
fun `when a name reference expression is selected, with a delegated property with dot qualified expression as root, property should be returned`() { |
||||
val ktPsiFactory = KtPsiFactory(project) |
||||
|
||||
@Language("Kotlin") |
||||
val template = """ |
||||
val repeatingAnimation = rememberInfiniteTransition() |
||||
|
||||
// Dot qualified expression |
||||
// | |
||||
// v |
||||
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( |
||||
property, |
||||
getRootElement(referenceExpression) |
||||
) |
||||
} |
||||
|
||||
|
||||
@Test |
||||
fun `when a name 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() |
||||
|
||||
// Dot qualified expression |
||||
// | |
||||
// v |
||||
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( |
||||
property, |
||||
getRootElement(referenceExpression) |
||||
) |
||||
} |
||||
|
||||
} |
Loading…
Reference in new issue