summaryrefslogtreecommitdiff
path: root/compose-ide-plugin
diff options
context:
space:
mode:
authorOscar Adame Vázquez <oscarad@google.com>2022-04-26 13:50:26 -0700
committerOscar Adame Vázquez <oscarad@google.com>2022-05-17 20:14:22 +0000
commitb8a7b788a8854d490d2cfab7396fbf7ab6b82a17 (patch)
tree3484a51adb0ba947589160b7bd77874c2f8beff5 /compose-ide-plugin
parent36dc86f8dc11e1c2c6877d5a9c211306c88e5328 (diff)
downloadidea-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')
-rw-r--r--compose-ide-plugin/src/com/android/tools/compose/code/completion/constraintlayout/InsertionFormat.kt4
-rw-r--r--compose-ide-plugin/src/com/android/tools/compose/code/completion/constraintlayout/JsonPsiUtil.kt27
-rw-r--r--compose-ide-plugin/src/com/android/tools/compose/code/completion/constraintlayout/provider/CompletionProviders.kt (renamed from compose-ide-plugin/src/com/android/tools/compose/code/completion/constraintlayout/provider/ConstraintSetCompletionProviders.kt)49
-rw-r--r--compose-ide-plugin/src/com/android/tools/compose/code/completion/constraintlayout/provider/model/BaseJsonElementModel.kt62
-rw-r--r--compose-ide-plugin/src/com/android/tools/compose/code/completion/constraintlayout/provider/model/ConstraintSetModel.kt22
-rw-r--r--compose-ide-plugin/src/com/android/tools/compose/code/completion/constraintlayout/provider/model/ConstraintSetsPropertyModel.kt8
-rw-r--r--compose-ide-plugin/src/com/android/tools/compose/code/completion/constraintlayout/provider/model/ConstraintsModel.kt2
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