Browse Source

integrated plugin and all its files

pull/1768/head
thelumiereguy 3 years ago
parent
commit
e80fdb3742
  1. 40
      idea-plugin/src/main/kotlin/org/jetbrains/compose/intentions/remove_composable/RemoveComposableIntention.kt
  2. 56
      idea-plugin/src/main/kotlin/org/jetbrains/compose/intentions/remove_parent_composable/RemoveParentComposableIntention.kt
  3. 8
      idea-plugin/src/main/kotlin/org/jetbrains/compose/intentions/utils/composable_finder/ComposableFunctionFinder.kt
  4. 42
      idea-plugin/src/main/kotlin/org/jetbrains/compose/intentions/utils/composable_finder/DeepComposableFunctionFinder.kt
  5. 34
      idea-plugin/src/main/kotlin/org/jetbrains/compose/intentions/utils/composable_finder/NestedComposableFinder.kt
  6. 39
      idea-plugin/src/main/kotlin/org/jetbrains/compose/intentions/utils/get_root_element/GetRootElement.kt
  7. 25
      idea-plugin/src/main/kotlin/org/jetbrains/compose/intentions/utils/is_intention_available/IsIntentionAvailable.kt
  8. 21
      idea-plugin/src/main/kotlin/org/jetbrains/compose/intentions/utils/is_psi_element_composable/IsPsiElementComposable.kt
  9. 72
      idea-plugin/src/main/kotlin/org/jetbrains/compose/intentions/wrap_with_composable/WrapWithComposableIntentionGroup.kt
  10. 54
      idea-plugin/src/main/kotlin/org/jetbrains/compose/intentions/wrap_with_composable/wrap_with_actions/BaseWrapWithComposableAction.kt
  11. 70
      idea-plugin/src/main/kotlin/org/jetbrains/compose/intentions/wrap_with_composable/wrap_with_actions/WrapWithActions.kt
  12. 26
      idea-plugin/src/main/resources/META-INF/plugin.xml
  13. 0
      idea-plugin/src/main/resources/intentionDescriptions/RemoveComposableIntention/after.kt.template
  14. 5
      idea-plugin/src/main/resources/intentionDescriptions/RemoveComposableIntention/before.kt.template
  15. 7
      idea-plugin/src/main/resources/intentionDescriptions/RemoveComposableIntention/description.html
  16. 4
      idea-plugin/src/main/resources/intentionDescriptions/RemoveParentComposableIntention/after.kt.template
  17. 6
      idea-plugin/src/main/resources/intentionDescriptions/RemoveParentComposableIntention/before.kt.template
  18. 7
      idea-plugin/src/main/resources/intentionDescriptions/RemoveParentComposableIntention/description.html
  19. 15
      idea-plugin/src/main/resources/intentionDescriptions/WrapWithComposableIntentionGroup/description.html
  20. 65
      idea-plugin/src/main/resources/templates/WrappedComposables.xml
  21. 216
      idea-plugin/src/test/kotlin/org/jetbrains/compose/intentions/utils/get_root_element/GetRootElementTest.kt

40
idea-plugin/src/main/kotlin/org/jetbrains/compose/intentions/remove_composable/RemoveComposableIntention.kt

@ -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
}

56
idea-plugin/src/main/kotlin/org/jetbrains/compose/intentions/remove_parent_composable/RemoveParentComposableIntention.kt

@ -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
}
}

8
idea-plugin/src/main/kotlin/org/jetbrains/compose/intentions/utils/composable_finder/ComposableFunctionFinder.kt

@ -0,0 +1,8 @@
package org.jetbrains.compose.intentions.utils.composable_finder
import com.intellij.psi.PsiElement
interface ComposableFunctionFinder {
fun isFunctionComposable(psiElement: PsiElement): Boolean
}

42
idea-plugin/src/main/kotlin/org/jetbrains/compose/intentions/utils/composable_finder/DeepComposableFunctionFinder.kt

@ -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()
}
}
}
}

34
idea-plugin/src/main/kotlin/org/jetbrains/compose/intentions/utils/composable_finder/NestedComposableFinder.kt

@ -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()
}
}

39
idea-plugin/src/main/kotlin/org/jetbrains/compose/intentions/utils/get_root_element/GetRootElement.kt

@ -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
}
}
}

25
idea-plugin/src/main/kotlin/org/jetbrains/compose/intentions/utils/is_intention_available/IsIntentionAvailable.kt

@ -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
}
}

21
idea-plugin/src/main/kotlin/org/jetbrains/compose/intentions/utils/is_psi_element_composable/IsPsiElementComposable.kt

@ -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()
}
}

72
idea-plugin/src/main/kotlin/org/jetbrains/compose/intentions/wrap_with_composable/WrapWithComposableIntentionGroup.kt

@ -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
}

54
idea-plugin/src/main/kotlin/org/jetbrains/compose/intentions/wrap_with_composable/wrap_with_actions/BaseWrapWithComposableAction.kt

@ -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?
}

70
idea-plugin/src/main/kotlin/org/jetbrains/compose/intentions/wrap_with_composable/wrap_with_actions/WrapWithActions.kt

@ -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")
}
}

26
idea-plugin/src/main/resources/META-INF/plugin.xml

@ -49,4 +49,30 @@
<action class="org.jetbrains.compose.desktop.ide.preview.RefreshOrRunPreviewAction"/>
</group>
</actions>
<extensions defaultExtensionNs="com.intellij">
<defaultLiveTemplates file="templates/WrappedComposables.xml"/>
<intentionAction id="wrap_group" order="first">
<className>org.jetbrains.compose.intentions.wrap_with_composable.WrapWithComposableIntentionGroup
</className>
<category>Composable intentions</category>
</intentionAction>
<intentionAction id="remove_composable" order="last">
<className>org.jetbrains.compose.intentions.remove_composable.RemoveComposableIntention
</className>
<category>Composable intentions</category>
</intentionAction>
<intentionAction id="remove_parent_composable" order="last">
<className>org.jetbrains.compose.intentions.remove_parent_composable.RemoveParentComposableIntention
</className>
<category>Composable intentions</category>
</intentionAction>
</extensions>
</idea-plugin>

0
idea-plugin/src/main/resources/intentionDescriptions/RemoveComposableIntention/after.kt.template

5
idea-plugin/src/main/resources/intentionDescriptions/RemoveComposableIntention/before.kt.template

@ -0,0 +1,5 @@
@Composable
fun Column() {
Text("Abc")
Text("Abc")
}

7
idea-plugin/src/main/resources/intentionDescriptions/RemoveComposableIntention/description.html

@ -0,0 +1,7 @@
<html lang="en">
<body>
<p>
A simple intention to remove the composable altogether.
</p>
</body>
</html>

4
idea-plugin/src/main/resources/intentionDescriptions/RemoveParentComposableIntention/after.kt.template

@ -0,0 +1,4 @@
@Composable
Button() {
Text("Abc")
}

6
idea-plugin/src/main/resources/intentionDescriptions/RemoveParentComposableIntention/before.kt.template

@ -0,0 +1,6 @@
@Composable
fun Column() {
Button(){
Text("Abc")
}
}

7
idea-plugin/src/main/resources/intentionDescriptions/RemoveParentComposableIntention/description.html

@ -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>

15
idea-plugin/src/main/resources/intentionDescriptions/WrapWithComposableIntentionGroup/description.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>

65
idea-plugin/src/main/resources/templates/WrappedComposables.xml

@ -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) {&#10; $SELECTION$ &#10; }"
toReformat="true" toShortenFQNames="true">
<variable name="COMPOSABLE" expression="" defaultValue="&quot;androidx.compose.foundation.layout.Box&quot;" 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) {&#10; $SELECTION$ &#10; }"
toReformat="true" toShortenFQNames="true">
<variable name="COMPOSABLE" expression="" defaultValue="&quot;androidx.compose.material.Card&quot;" 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) {&#10; $SELECTION$ &#10; }"
toReformat="true" toShortenFQNames="true">
<variable name="COMPOSABLE" expression="" defaultValue="&quot;androidx.compose.foundation.layout.Column&quot;" 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) {&#10; $SELECTION$ &#10; }"
toReformat="true" toShortenFQNames="true">
<variable name="COMPOSABLE" expression="" defaultValue="&quot;androidx.compose.foundation.layout.Row&quot;" 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 {&#10; $SELECTION$ &#10;} }"
toReformat="true" toShortenFQNames="true">
<variable name="COMPOSABLE" expression="" defaultValue="&quot;androidx.compose.foundation.lazy.LazyColumn&quot;" 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 {&#10; $SELECTION$ &#10;} }"
toReformat="true" toShortenFQNames="true">
<variable name="COMPOSABLE" expression="" defaultValue="&quot;androidx.compose.foundation.lazy.LazyRow&quot;" alwaysStopAt="true"/>
<context>
<option name="KOTLIN" value="true"/>
<option name="KOTLIN_COMMENT" value="false"/>
</context>
</template>
</templateSet>

216
idea-plugin/src/test/kotlin/org/jetbrains/compose/intentions/utils/get_root_element/GetRootElementTest.kt

@ -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…
Cancel
Save