diff options
author | Oscar Adame Vázquez <oscarad@google.com> | 2022-04-26 13:50:26 -0700 |
---|---|---|
committer | Oscar Adame Vázquez <oscarad@google.com> | 2022-05-17 20:14:22 +0000 |
commit | b8a7b788a8854d490d2cfab7396fbf7ab6b82a17 (patch) | |
tree | 3484a51adb0ba947589160b7bd77874c2f8beff5 /compose-ide-plugin | |
parent | 36dc86f8dc11e1c2c6877d5a9c211306c88e5328 (diff) | |
download | idea-b8a7b788a8854d490d2cfab7396fbf7ab6b82a17.tar.gz |
[Compose-CL] Refactor JsonProperty usage
Refactored an InsertionFormat declaration into InsertionFormat.kt
Renamed ConstraintSetCompletionProviders.kt to CompletionProviders.kt
Renamed BaseJsonPropertyModel to JsonPropertyModel.
Refactored common patterns used with JsonProperty elements into
JsonPropertyModel.
The typical way to obtain the parent JsonProperty of a completion
performed on another JsonProperty is now part of the companion object of
JsonPropertyModel.
A typical use case to know which other properties have been declared is
now on JsonPropertyModel.declaredFieldNamesSet
JsonPropertyModel may also return the name of the property.
Bug: 207030860
Test: unaffected
Change-Id: I393e488b003d472a427dadb8a0228fa01948ad3e
Diffstat (limited to 'compose-ide-plugin')
7 files changed, 125 insertions, 49 deletions
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 7c791eb8372..55365c909df 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 @@ -28,3 +28,7 @@ internal val JsonNewObjectTemplate = LiteralNewLineFormat(": {\n}") internal val JsonStringArrayTemplate = LiteralWithCaretFormat(": ['|'],") internal val ConstrainAnchorTemplate = LiveTemplateFormat(": ['<>', '<>', <0>],") + +internal val ClearAllTemplate = LiteralWithCaretFormat( + literalFormat = ": ['${ClearOption.Constraints}', '${ClearOption.Dimensions}', '${ClearOption.Transforms}']," +)
\ No newline at end of file diff --git a/compose-ide-plugin/src/com/android/tools/compose/code/completion/constraintlayout/JsonPsiUtil.kt b/compose-ide-plugin/src/com/android/tools/compose/code/completion/constraintlayout/JsonPsiUtil.kt new file mode 100644 index 00000000000..29a5b452278 --- /dev/null +++ b/compose-ide-plugin/src/com/android/tools/compose/code/completion/constraintlayout/JsonPsiUtil.kt @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.tools.compose.code.completion.constraintlayout + +import com.intellij.codeInsight.completion.CompletionParameters +import com.intellij.json.psi.JsonProperty +import com.intellij.psi.util.parentOfType + +/** + * From the element being invoked, returns the [JsonProperty] parent that also includes the [JsonProperty] from which completion is + * triggered. + */ +internal fun getJsonPropertyParent(parameters: CompletionParameters): JsonProperty? = + parameters.position.parentOfType<JsonProperty>(withSelf = true)?.parentOfType<JsonProperty>(withSelf = false)
\ No newline at end of file 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/CompletionProviders.kt index 4e5260d739e..512c432af00 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/CompletionProviders.kt @@ -15,6 +15,7 @@ */ package com.android.tools.compose.code.completion.constraintlayout.provider +import com.android.tools.compose.code.completion.constraintlayout.ClearAllTemplate 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 @@ -28,13 +29,12 @@ import com.android.tools.compose.code.completion.constraintlayout.RenderTransfor import com.android.tools.compose.code.completion.constraintlayout.SpecialAnchor import com.android.tools.compose.code.completion.constraintlayout.StandardAnchor import com.android.tools.compose.code.completion.constraintlayout.TransitionField -import com.android.tools.compose.code.completion.constraintlayout.provider.model.BaseJsonPropertyModel +import com.android.tools.compose.code.completion.constraintlayout.getJsonPropertyParent import com.android.tools.compose.code.completion.constraintlayout.provider.model.ConstraintSetModel import com.android.tools.compose.code.completion.constraintlayout.provider.model.ConstraintSetsPropertyModel -import com.android.tools.compose.code.completion.constraintlayout.provider.model.ConstraintsModel +import com.android.tools.compose.code.completion.constraintlayout.provider.model.JsonPropertyModel 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 @@ -107,7 +107,7 @@ internal object ConstraintSetFieldsProvider : BaseConstraintSetsCompletionProvid parameters: CompletionParameters, result: CompletionResultSet ) { - val currentConstraintSet = getJsonPropertyParent(parameters)?.let { ConstraintSetModel(it) } ?: return + val currentConstraintSet = ConstraintSetModel.getModelForCompletionOnConstraintSetProperty(parameters) ?: return val currentSetName = currentConstraintSet.name ?: return constraintSetsPropertyModel.getRemainingFieldsForConstraintSet(currentSetName).forEach { fieldName -> val template = if (fieldName == KeyWords.Extends) JsonStringValueTemplate else JsonNewObjectTemplate @@ -125,7 +125,7 @@ internal object ConstraintSetNamesProvider : BaseConstraintSetsCompletionProvide parameters: CompletionParameters, result: CompletionResultSet ) { - val currentConstraintSet = getJsonPropertyParent(parameters)?.let { ConstraintSetModel(it) } + val currentConstraintSet = ConstraintSetModel.getModelForCompletionOnConstraintSetProperty(parameters) val currentSetName = currentConstraintSet?.name val names = constraintSetsPropertyModel.getConstraintSetNames().toMutableSet() if (currentSetName != null) { @@ -144,33 +144,28 @@ internal object ConstraintsProvider : BaseConstraintSetsCompletionProvider() { parameters: CompletionParameters, result: CompletionResultSet ) { - val jsonPropertyParent = getJsonPropertyParent(parameters) - val currentConstraintsModel = jsonPropertyParent?.let { ConstraintsModel(it) } - val existingFields = currentConstraintsModel?.declaredFieldNames?.toHashSet() ?: emptySet<String>() + val parentPropertyModel = JsonPropertyModel.getModelForCompletionOnInnerJsonProperty(parameters) ?: return + val existingFieldsSet = parentPropertyModel.declaredFieldNamesSet StandardAnchor.values().forEach { - if (!existingFields.contains(it.keyWord)) { + if (!existingFieldsSet.contains(it.keyWord)) { result.addLookupElement(lookupString = it.keyWord, tailText = " [...]", format = ConstrainAnchorTemplate) } } - if (!existingFields.contains(KeyWords.Visibility)) { + if (!existingFieldsSet.contains(KeyWords.Visibility)) { result.addLookupElement(lookupString = KeyWords.Visibility, format = JsonStringValueTemplate) } - result.addEnumKeyWordsWithStringValueTemplate<SpecialAnchor>(existingFields) - result.addEnumKeyWordsWithNumericValueTemplate<Dimension>(existingFields) - result.addEnumKeyWordsWithNumericValueTemplate<RenderTransform>(existingFields) + result.addEnumKeyWordsWithStringValueTemplate<SpecialAnchor>(existingFieldsSet) + result.addEnumKeyWordsWithNumericValueTemplate<Dimension>(existingFieldsSet) + result.addEnumKeyWordsWithNumericValueTemplate<RenderTransform>(existingFieldsSet) // Complete 'clear' if the containing ConstraintSet has `extendsFrom` - val containingConstraintSetModel = jsonPropertyParent?.parentOfType<JsonProperty>(withSelf = false)?.let { ConstraintSetModel(it) } + val containingConstraintSetModel = parentPropertyModel.getParentProperty()?.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}']," - ) - ) + result.addLookupElement(lookupString = KeyWords.Clear, format = ClearAllTemplate, tailText = " [<all>]") } } } @@ -247,10 +242,9 @@ internal object ClearOptionsProvider : BaseConstraintSetsCompletionProvider() { */ internal object TransitionFieldsProvider : CompletionProvider<CompletionParameters>() { override fun addCompletions(parameters: CompletionParameters, context: ProcessingContext, result: CompletionResultSet) { - val transitionPropertyModel = getJsonPropertyParent(parameters)?.let { BaseJsonPropertyModel(it) } - val existing = transitionPropertyModel?.declaredFieldNames?.toHashSet() ?: emptySet() + val parentPropertyModel = JsonPropertyModel.getModelForCompletionOnInnerJsonProperty(parameters) ?: return TransitionField.values().forEach { - if (existing.contains(it.keyWord)) { + if (parentPropertyModel.containsPropertyOfName(it.keyWord)) { // skip return@forEach } @@ -282,13 +276,6 @@ internal class EnumValuesCompletionProvider<E>(private val enumClass: KClass<E>) } /** - * From the element being invoked, returns the [JsonProperty] parent that also includes the [JsonProperty] from which completion is - * triggered. - */ -private fun getJsonPropertyParent(parameters: CompletionParameters): JsonProperty? = - parameters.position.parentOfType<JsonProperty>(withSelf = true)?.parentOfType<JsonProperty>(withSelf = false) - -/** * Add the [ConstraintLayoutKeyWord.keyWord] of the enum constants as a completion result that takes a string for its value. */ private inline fun <reified E> CompletionResultSet.addEnumKeyWordsWithStringValueTemplate( diff --git a/compose-ide-plugin/src/com/android/tools/compose/code/completion/constraintlayout/provider/model/BaseJsonElementModel.kt b/compose-ide-plugin/src/com/android/tools/compose/code/completion/constraintlayout/provider/model/BaseJsonElementModel.kt index 575eb10197d..16e74dd8c8f 100644 --- a/compose-ide-plugin/src/com/android/tools/compose/code/completion/constraintlayout/provider/model/BaseJsonElementModel.kt +++ b/compose-ide-plugin/src/com/android/tools/compose/code/completion/constraintlayout/provider/model/BaseJsonElementModel.kt @@ -15,10 +15,14 @@ */ package com.android.tools.compose.code.completion.constraintlayout.provider.model +import com.android.tools.compose.code.completion.constraintlayout.getJsonPropertyParent +import com.intellij.codeInsight.completion.CompletionParameters import com.intellij.json.psi.JsonElement import com.intellij.json.psi.JsonObject import com.intellij.json.psi.JsonProperty +import com.intellij.openapi.progress.ProgressManager import com.intellij.psi.SmartPointerManager +import com.intellij.psi.util.parentOfType import org.jetbrains.kotlin.psi.psiUtil.getChildOfType /** @@ -33,21 +37,65 @@ internal abstract class BaseJsonElementModel<E: JsonElement>(element: E) { * * Populates some common fields and provides useful function while avoiding holding to PsiElement instances. */ -internal open class BaseJsonPropertyModel(element: JsonProperty): BaseJsonElementModel<JsonProperty>(element) { +internal open class JsonPropertyModel(element: JsonProperty): BaseJsonElementModel<JsonProperty>(element) { + /** + * The [JsonObject] that describes this [JsonProperty]. + */ + private val innerJsonObject: JsonObject? = elementPointer.element?.getChildOfType<JsonObject>() + + /** + * A mapping of the containing [JsonProperty]s by their declare name. + */ + private val propertiesByName: Map<String, JsonProperty> = + innerJsonObject?.propertyList?.associateBy { it.name } ?: emptyMap() + /** * [List] of all the children of this element that are [JsonProperty]. */ - protected val innerProperties: List<JsonProperty> = - elementPointer.element?.getChildOfType<JsonObject>()?.propertyList?.toList() ?: emptyList() + protected val innerProperties: Collection<JsonProperty> = propertiesByName.values + + /** + * Name of the [JsonProperty]. + */ + val name: String? + get() = elementPointer.element?.name /** - * Names of all declared properties in this Json. + * A set of names for all declared properties in this [JsonProperty]. */ - val declaredFieldNames: List<String> = innerProperties.map { it.name } + val declaredFieldNamesSet: Set<String> = propertiesByName.keys /** * For the children of the current element, returns the [JsonProperty] which name matches the given [name]. Null if none of them does. */ - protected fun findProperty(name: String): JsonProperty? = - innerProperties.firstOrNull { it.name == name } + protected fun findProperty(name: String): JsonProperty? = propertiesByName[name] + + /** + * Returns true if this [JsonProperty] contains another [JsonProperty] declared by the given [name]. + */ + fun containsPropertyOfName(name: String): Boolean = propertiesByName.containsKey(name) + + /** + * Returns the containing [JsonProperty]. + * + * May return null if this model is for a top level [JsonProperty]. + */ + fun getParentProperty(): JsonProperty? = elementPointer.element?.parentOfType<JsonProperty>(withSelf = false) + + companion object { + /** + * Returns the [JsonPropertyModel] where the completion is performed on an inner [JsonProperty], including if the completion is on the + * value side of the inner [JsonProperty]. + * + * In other words, the model of the second [JsonProperty] parent if the element on [CompletionParameters.getPosition] is NOT a + * [JsonProperty]. + * + * Or the model of the first [JsonProperty] parent if the element on [CompletionParameters.getPosition] is a [JsonProperty]. + */ + fun getModelForCompletionOnInnerJsonProperty(parameters: CompletionParameters): JsonPropertyModel? { + val parentJsonProperty = getJsonPropertyParent(parameters) ?: return null + ProgressManager.checkCanceled() + return JsonPropertyModel(parentJsonProperty) + } + } }
\ No newline at end of file diff --git a/compose-ide-plugin/src/com/android/tools/compose/code/completion/constraintlayout/provider/model/ConstraintSetModel.kt b/compose-ide-plugin/src/com/android/tools/compose/code/completion/constraintlayout/provider/model/ConstraintSetModel.kt index 26f45edb9f5..95fd5db4b53 100644 --- a/compose-ide-plugin/src/com/android/tools/compose/code/completion/constraintlayout/provider/model/ConstraintSetModel.kt +++ b/compose-ide-plugin/src/com/android/tools/compose/code/completion/constraintlayout/provider/model/ConstraintSetModel.kt @@ -16,26 +16,24 @@ package com.android.tools.compose.code.completion.constraintlayout.provider.model import com.android.tools.compose.code.completion.constraintlayout.KeyWords +import com.android.tools.compose.code.completion.constraintlayout.getJsonPropertyParent +import com.intellij.codeInsight.completion.CompletionParameters import com.intellij.json.psi.JsonProperty import com.intellij.json.psi.JsonStringLiteral +import com.intellij.openapi.progress.ProgressManager /** * Model for the JSON block corresponding to a single ConstraintSet. * * A ConstraintSet is a state that defines a specific layout of the contents in a ConstraintLayout. */ -internal class ConstraintSetModel(jsonProperty: JsonProperty) : BaseJsonPropertyModel(jsonProperty) { +internal class ConstraintSetModel(jsonProperty: JsonProperty) : JsonPropertyModel(jsonProperty) { /** * List of properties that have a constraint block assigned to it. */ private val propertiesWithConstraints = innerProperties.filter { it.name != KeyWords.Extends } /** - * Name of the ConstraintSet. - */ - val name: String? = elementPointer.element?.name - - /** * Name of the ConstraintSet this is extending constraints from. */ val extendsFrom: String? = (findProperty(KeyWords.Extends)?.value as? JsonStringLiteral)?.value @@ -57,4 +55,16 @@ internal class ConstraintSetModel(jsonProperty: JsonProperty) : BaseJsonProperty // TODO(b/207030860): Add a method that can pull all resolved constraints for each widget ID, it could be useful to make sure we are not // offering options that are implicitly present from the 'Extends' ConstraintSet + + companion object { + /** + * Returns a [ConstraintSetModel], for when the completion is performed on a property or the value of a property within a ConstraintSet + * declaration. + */ + fun getModelForCompletionOnConstraintSetProperty(parameters: CompletionParameters): ConstraintSetModel? { + val parentJsonProperty = getJsonPropertyParent(parameters) ?: return null + ProgressManager.checkCanceled() + return ConstraintSetModel(parentJsonProperty) + } + } }
\ No newline at end of file diff --git a/compose-ide-plugin/src/com/android/tools/compose/code/completion/constraintlayout/provider/model/ConstraintSetsPropertyModel.kt b/compose-ide-plugin/src/com/android/tools/compose/code/completion/constraintlayout/provider/model/ConstraintSetsPropertyModel.kt index 0bfde8c3ce6..0d16fa7140c 100644 --- a/compose-ide-plugin/src/com/android/tools/compose/code/completion/constraintlayout/provider/model/ConstraintSetsPropertyModel.kt +++ b/compose-ide-plugin/src/com/android/tools/compose/code/completion/constraintlayout/provider/model/ConstraintSetsPropertyModel.kt @@ -28,7 +28,7 @@ import com.intellij.json.psi.JsonProperty */ internal class ConstraintSetsPropertyModel( constraintSetsElement: JsonProperty -) : BaseJsonPropertyModel(constraintSetsElement) { +) : JsonPropertyModel(constraintSetsElement) { // TODO(b/209839226): Explore how we could use these models to validate the syntax or structure of the JSON as well as to check logic // correctness through Inspections/Lint /** @@ -39,8 +39,8 @@ internal class ConstraintSetsPropertyModel( /** * The names of all ConstraintSets in this block. */ - fun getConstraintSetNames(): List<String> { - return declaredFieldNames + fun getConstraintSetNames(): Collection<String> { + return declaredFieldNamesSet } /** @@ -51,7 +51,7 @@ internal class ConstraintSetsPropertyModel( val availableNames = mutableSetOf(KeyWords.Extends) val usedNames = mutableSetOf<String>() constraintSets.forEach { constraintSet -> - constraintSet.declaredFieldNames.forEach { propertyName -> + constraintSet.declaredFieldNamesSet.forEach { propertyName -> if (constraintSet.name == constraintSetName) { usedNames.add(propertyName) } diff --git a/compose-ide-plugin/src/com/android/tools/compose/code/completion/constraintlayout/provider/model/ConstraintsModel.kt b/compose-ide-plugin/src/com/android/tools/compose/code/completion/constraintlayout/provider/model/ConstraintsModel.kt index 7417d159a5e..4370246d874 100644 --- a/compose-ide-plugin/src/com/android/tools/compose/code/completion/constraintlayout/provider/model/ConstraintsModel.kt +++ b/compose-ide-plugin/src/com/android/tools/compose/code/completion/constraintlayout/provider/model/ConstraintsModel.kt @@ -23,7 +23,7 @@ import com.intellij.json.psi.JsonProperty * Constraints are a set of instructions that define the widget's dimensions, position with respect to other widgets and render-time * transforms. */ -internal class ConstraintsModel(jsonProperty: JsonProperty): BaseJsonPropertyModel(jsonProperty) { +internal class ConstraintsModel(jsonProperty: JsonProperty): JsonPropertyModel(jsonProperty) { // TODO(b/207030860): Fill the contents of this model as is necessary, keeping in mind that it would be useful to have fields like // 'verticalConstraints', 'hasBaseline', 'dimensionBehavior', etc... }
\ No newline at end of file |