summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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