summaryrefslogtreecommitdiff
path: root/compose-ide-plugin
diff options
context:
space:
mode:
authorOscar Adame Vázquez <oscarad@google.com>2022-04-25 15:09:16 -0700
committerOscar Adame Vázquez <oscarad@google.com>2022-05-09 21:41:59 +0000
commit18ea8548f70d11fb62b95b4d3f739735be4ad842 (patch)
tree264ff0dff53ca982115980dbad16d9d26625790a /compose-ide-plugin
parent75e07da224e1044d5d3c6790f681391dfcdbe566 (diff)
downloadidea-18ea8548f70d11fb62b95b4d3f739735be4ad842.tar.gz
[Compose-CL] Complete 'clear' field and 'clear' options
When a ConstraintSet has the 'Extends' property populated, each Constraint block may decide to clear inherited properties. These properties are grouped by the options 'constraints', 'dimensions' and 'transforms'. This makes it so that the 'clear' property and its values are completed when possible. Bug: 207030860 Test: ConstraintLayoutJsonCompletionConstributorTest Change-Id: I914ff7090555428aa2af8c49523e22af08da804b
Diffstat (limited to 'compose-ide-plugin')
-rw-r--r--compose-ide-plugin/src/com/android/tools/compose/code/completion/constraintlayout/Constants.kt15
-rw-r--r--compose-ide-plugin/src/com/android/tools/compose/code/completion/constraintlayout/ConstraintLayoutJsonCompletionContributor.kt8
-rw-r--r--compose-ide-plugin/src/com/android/tools/compose/code/completion/constraintlayout/InsertionFormat.kt2
-rw-r--r--compose-ide-plugin/src/com/android/tools/compose/code/completion/constraintlayout/PatternUtils.kt21
-rw-r--r--compose-ide-plugin/src/com/android/tools/compose/code/completion/constraintlayout/provider/ConstraintSetCompletionProviders.kt41
-rw-r--r--compose-ide-plugin/testSrc/com/android/tools/compose/code/completion/constraintlayout/ConstraintLayoutJsonCompletionContributorTest.kt53
6 files changed, 136 insertions, 4 deletions
diff --git a/compose-ide-plugin/src/com/android/tools/compose/code/completion/constraintlayout/Constants.kt b/compose-ide-plugin/src/com/android/tools/compose/code/completion/constraintlayout/Constants.kt
index c51ae159458..4e85fc54c40 100644
--- a/compose-ide-plugin/src/com/android/tools/compose/code/completion/constraintlayout/Constants.kt
+++ b/compose-ide-plugin/src/com/android/tools/compose/code/completion/constraintlayout/Constants.kt
@@ -41,6 +41,15 @@ internal object KeyWords {
* Name of the Visibility property in a constraint block.
*/
const val Visibility = "visibility"
+
+ /**
+ * Name of the Clear property in a constraint block.
+ *
+ * Populated by an array of options to clear inherited parameters from [Extends].
+ *
+ * @see ClearOption
+ */
+ const val Clear = "clear"
}
/**
@@ -124,6 +133,12 @@ internal enum class VisibilityMode(override val keyWord: String): ConstraintLayo
Gone("gone")
}
+internal enum class ClearOption(override val keyWord: String): ConstraintLayoutKeyWord {
+ Constraints("constraints"),
+ Dimensions("dimensions"),
+ Transforms("transforms")
+}
+
internal enum class TransitionField(override val keyWord: String): ConstraintLayoutKeyWord {
From("from"),
To("to"),
diff --git a/compose-ide-plugin/src/com/android/tools/compose/code/completion/constraintlayout/ConstraintLayoutJsonCompletionContributor.kt b/compose-ide-plugin/src/com/android/tools/compose/code/completion/constraintlayout/ConstraintLayoutJsonCompletionContributor.kt
index ea381924903..6e0faca1b5b 100644
--- a/compose-ide-plugin/src/com/android/tools/compose/code/completion/constraintlayout/ConstraintLayoutJsonCompletionContributor.kt
+++ b/compose-ide-plugin/src/com/android/tools/compose/code/completion/constraintlayout/ConstraintLayoutJsonCompletionContributor.kt
@@ -16,6 +16,7 @@
package com.android.tools.compose.code.completion.constraintlayout
import com.android.tools.compose.code.completion.constraintlayout.provider.AnchorablesProvider
+import com.android.tools.compose.code.completion.constraintlayout.provider.ClearOptionsProvider
import com.android.tools.compose.code.completion.constraintlayout.provider.ConstraintIdsProvider
import com.android.tools.compose.code.completion.constraintlayout.provider.ConstraintSetFieldsProvider
import com.android.tools.compose.code.completion.constraintlayout.provider.ConstraintSetNamesProvider
@@ -100,6 +101,13 @@ class ConstraintLayoutJsonCompletionContributor : CompletionContributor() {
)
extend(
CompletionType.BASIC,
+ // Complete a clear option within the 'clear' array
+ jsonStringValue()
+ .insideClearArray(),
+ ClearOptionsProvider
+ )
+ extend(
+ CompletionType.BASIC,
// Complete non-numeric dimension values for width & height
jsonStringValue()
.withPropertyParentAtLevel(BASE_DEPTH_FOR_LITERAL_IN_PROPERTY, Dimension.values().map { it.keyWord }),
diff --git a/compose-ide-plugin/src/com/android/tools/compose/code/completion/constraintlayout/InsertionFormat.kt b/compose-ide-plugin/src/com/android/tools/compose/code/completion/constraintlayout/InsertionFormat.kt
index 25bb04254af..7c791eb8372 100644
--- a/compose-ide-plugin/src/com/android/tools/compose/code/completion/constraintlayout/InsertionFormat.kt
+++ b/compose-ide-plugin/src/com/android/tools/compose/code/completion/constraintlayout/InsertionFormat.kt
@@ -25,4 +25,6 @@ internal val JsonNumericValueTemplate = LiteralWithCaretFormat(": |,")
internal val JsonNewObjectTemplate = LiteralNewLineFormat(": {\n}")
+internal val JsonStringArrayTemplate = LiteralWithCaretFormat(": ['|'],")
+
internal val ConstrainAnchorTemplate = LiveTemplateFormat(": ['<>', '<>', <0>],")
diff --git a/compose-ide-plugin/src/com/android/tools/compose/code/completion/constraintlayout/PatternUtils.kt b/compose-ide-plugin/src/com/android/tools/compose/code/completion/constraintlayout/PatternUtils.kt
index f7c73049e22..2937e7af8b5 100644
--- a/compose-ide-plugin/src/com/android/tools/compose/code/completion/constraintlayout/PatternUtils.kt
+++ b/compose-ide-plugin/src/com/android/tools/compose/code/completion/constraintlayout/PatternUtils.kt
@@ -25,6 +25,7 @@ import com.intellij.patterns.PatternCondition
import com.intellij.patterns.PlatformPatterns
import com.intellij.patterns.PsiElementPattern
import com.intellij.patterns.StandardPatterns
+import com.intellij.patterns.StringPattern
import com.intellij.psi.PsiElement
import com.intellij.util.ProcessingContext
@@ -37,13 +38,27 @@ internal fun jsonStringValue() =
internal fun PsiElementPattern<*, *>.withConstraintSetsParentAtLevel(level: Int) = withPropertyParentAtLevel(level, KeyWords.ConstraintSets)
internal fun PsiElementPattern<*, *>.withTransitionsParentAtLevel(level: Int) = withPropertyParentAtLevel(level, KeyWords.Transitions)
-internal fun PsiElementPattern<*, *>.insideConstraintArray() =
+internal fun PsiElementPattern<*, *>.insideClearArray() = inArrayWithinConstraintBlockProperty {
+ // For the 'clear' constraint block property
+ matches(KeyWords.Clear)
+}
+
+internal fun PsiElementPattern<*, *>.insideConstraintArray() = inArrayWithinConstraintBlockProperty {
+ // The parent property name may only be a StandardAnchor
+ oneOf(StandardAnchor.values().map { it.keyWord })
+}
+
+/**
+ * [PsiElementPattern] that matches an element in a [JsonArray] within a Constraint block. Where the property the array is assigned to, has
+ * a name that is matched by [matchPropertyName].
+ */
+internal fun PsiElementPattern<*, *>.inArrayWithinConstraintBlockProperty(matchPropertyName: StringPattern.() -> StringPattern) =
withSuperParent(2, psiElement<JsonArray>())
.withSuperParent(
BASE_DEPTH_FOR_LITERAL_IN_PROPERTY + 1, // JsonArray adds one level
psiElement<JsonProperty>().withChild(
- // The parent property name may only be a StandardAnchor
- psiElement<JsonReferenceExpression>().withText(StandardPatterns.string().oneOf(StandardAnchor.values().map { it.keyWord }))
+ // The first expression in a JsonProperty corresponds to the name of the property
+ psiElement<JsonReferenceExpression>().withText(StandardPatterns.string().matchPropertyName())
)
)
.withConstraintSetsParentAtLevel(CONSTRAINT_BLOCK_PROPERTY_DEPTH + 1) // JsonArray adds one level
diff --git a/compose-ide-plugin/src/com/android/tools/compose/code/completion/constraintlayout/provider/ConstraintSetCompletionProviders.kt b/compose-ide-plugin/src/com/android/tools/compose/code/completion/constraintlayout/provider/ConstraintSetCompletionProviders.kt
index 373677956ac..4e5260d739e 100644
--- a/compose-ide-plugin/src/com/android/tools/compose/code/completion/constraintlayout/provider/ConstraintSetCompletionProviders.kt
+++ b/compose-ide-plugin/src/com/android/tools/compose/code/completion/constraintlayout/provider/ConstraintSetCompletionProviders.kt
@@ -15,11 +15,13 @@
*/
package com.android.tools.compose.code.completion.constraintlayout.provider
+import com.android.tools.compose.code.completion.constraintlayout.ClearOption
import com.android.tools.compose.code.completion.constraintlayout.ConstrainAnchorTemplate
import com.android.tools.compose.code.completion.constraintlayout.ConstraintLayoutKeyWord
import com.android.tools.compose.code.completion.constraintlayout.Dimension
import com.android.tools.compose.code.completion.constraintlayout.JsonNewObjectTemplate
import com.android.tools.compose.code.completion.constraintlayout.JsonNumericValueTemplate
+import com.android.tools.compose.code.completion.constraintlayout.JsonStringArrayTemplate
import com.android.tools.compose.code.completion.constraintlayout.JsonStringValueTemplate
import com.android.tools.compose.code.completion.constraintlayout.KeyWords
import com.android.tools.compose.code.completion.constraintlayout.RenderTransform
@@ -32,11 +34,14 @@ import com.android.tools.compose.code.completion.constraintlayout.provider.model
import com.android.tools.compose.code.completion.constraintlayout.provider.model.ConstraintsModel
import com.android.tools.compose.completion.addLookupElement
import com.android.tools.compose.completion.inserthandler.InsertionFormat
+import com.android.tools.compose.completion.inserthandler.LiteralWithCaretFormat
import com.intellij.codeInsight.completion.CompletionParameters
import com.intellij.codeInsight.completion.CompletionProvider
import com.intellij.codeInsight.completion.CompletionResultSet
+import com.intellij.json.psi.JsonArray
import com.intellij.json.psi.JsonObject
import com.intellij.json.psi.JsonProperty
+import com.intellij.json.psi.JsonStringLiteral
import com.intellij.openapi.progress.ProgressManager
import com.intellij.psi.PsiElement
import com.intellij.psi.util.parentOfType
@@ -139,7 +144,8 @@ internal object ConstraintsProvider : BaseConstraintSetsCompletionProvider() {
parameters: CompletionParameters,
result: CompletionResultSet
) {
- val currentConstraintsModel = getJsonPropertyParent(parameters)?.let { ConstraintsModel(it) }
+ val jsonPropertyParent = getJsonPropertyParent(parameters)
+ val currentConstraintsModel = jsonPropertyParent?.let { ConstraintsModel(it) }
val existingFields = currentConstraintsModel?.declaredFieldNames?.toHashSet() ?: emptySet<String>()
StandardAnchor.values().forEach {
if (!existingFields.contains(it.keyWord)) {
@@ -152,6 +158,20 @@ internal object ConstraintsProvider : BaseConstraintSetsCompletionProvider() {
result.addEnumKeyWordsWithStringValueTemplate<SpecialAnchor>(existingFields)
result.addEnumKeyWordsWithNumericValueTemplate<Dimension>(existingFields)
result.addEnumKeyWordsWithNumericValueTemplate<RenderTransform>(existingFields)
+
+ // Complete 'clear' if the containing ConstraintSet has `extendsFrom`
+ val containingConstraintSetModel = jsonPropertyParent?.parentOfType<JsonProperty>(withSelf = false)?.let { ConstraintSetModel(it) }
+ if (containingConstraintSetModel?.extendsFrom != null) {
+ // Add an option with an empty string array and another one with all clear options
+ result.addLookupElement(lookupString = KeyWords.Clear, format = JsonStringArrayTemplate)
+ result.addLookupElement(
+ lookupString = KeyWords.Clear,
+ tailText = " [<all>]",
+ format = LiteralWithCaretFormat(
+ literalFormat = ": ['${ClearOption.Constraints}', '${ClearOption.Dimensions}', '${ClearOption.Transforms}'],"
+ )
+ )
+ }
}
}
@@ -202,6 +222,25 @@ internal object AnchorablesProvider : BaseConstraintSetsCompletionProvider() {
}
/**
+ * Provides the appropriate options when completing string literals within a `clear` array.
+ *
+ * @see ClearOption
+ */
+internal object ClearOptionsProvider : BaseConstraintSetsCompletionProvider() {
+ override fun addCompletions(
+ constraintSetsPropertyModel: ConstraintSetsPropertyModel,
+ parameters: CompletionParameters,
+ result: CompletionResultSet
+ ) {
+ val existing = parameters.position.parentOfType<JsonArray>(withSelf = false)?.valueList
+ ?.filterIsInstance<JsonStringLiteral>()
+ ?.map { it.value }
+ ?.toSet() ?: emptySet()
+ addEnumKeywords<ClearOption>(result, existing)
+ }
+}
+
+/**
* Provides completion for the fields of a `Transition`.
*
* @see TransitionField
diff --git a/compose-ide-plugin/testSrc/com/android/tools/compose/code/completion/constraintlayout/ConstraintLayoutJsonCompletionContributorTest.kt b/compose-ide-plugin/testSrc/com/android/tools/compose/code/completion/constraintlayout/ConstraintLayoutJsonCompletionContributorTest.kt
index 4a0789442bd..597ee5f773c 100644
--- a/compose-ide-plugin/testSrc/com/android/tools/compose/code/completion/constraintlayout/ConstraintLayoutJsonCompletionContributorTest.kt
+++ b/compose-ide-plugin/testSrc/com/android/tools/compose/code/completion/constraintlayout/ConstraintLayoutJsonCompletionContributorTest.kt
@@ -358,6 +358,59 @@ internal class ConstraintLayoutJsonCompletionContributorTest {
""".trimIndent())
assertThat(myFixture.lookupElementStrings!!).containsExactly("e", "f", "g", "h")
}
+
+ @Test
+ fun completeClearField() {
+ myFixture.completeJson5Text("""
+ {
+ ConstraintSets: {
+ a: {},
+ b: {
+ Extends: 'a',
+ box: {
+ clea$caret
+ }
+ },
+ }
+ }
+ """.trimIndent())
+ // The repeated clear is to autocomplete with all options populated
+ assertThat(myFixture.lookupElementStrings!!).containsExactly("clear", "clear")
+ }
+
+ @Test
+ fun completeClearOptions() {
+ myFixture.completeJson5Text("""
+ {
+ ConstraintSets: {
+ a: {},
+ b: {
+ Extends: 'a',
+ box: {
+ clear: ['$caret'],
+ }
+ },
+ }
+ }
+ """.trimIndent())
+ assertThat(myFixture.lookupElementStrings!!).containsExactly("constraints", "dimensions", "transforms")
+
+ myFixture.completeJson5Text("""
+ {
+ ConstraintSets: {
+ a: {},
+ b: {
+ Extends: 'a',
+ box: {
+ clear: ['constraints', '$caret'],
+ }
+ },
+ }
+ }
+ """.trimIndent())
+ // 'constraints' options is already populated
+ assertThat(myFixture.lookupElementStrings!!).containsExactly("dimensions", "transforms")
+ }
}
private fun CodeInsightTestFixture.completeJson5Text(@Language("JSON5") text: String) {