diff options
author | Oscar Adame Vázquez <oscarad@google.com> | 2022-04-25 15:09:16 -0700 |
---|---|---|
committer | Oscar Adame Vázquez <oscarad@google.com> | 2022-05-09 21:41:59 +0000 |
commit | 18ea8548f70d11fb62b95b4d3f739735be4ad842 (patch) | |
tree | 264ff0dff53ca982115980dbad16d9d26625790a /compose-ide-plugin | |
parent | 75e07da224e1044d5d3c6790f681391dfcdbe566 (diff) | |
download | idea-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')
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) { |