mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-10-06 06:21:16 +07:00
project configuration DSL: fix schema for run configurations - use oneOf
This commit is contained in:
@@ -15,29 +15,23 @@ internal inline fun StringBuilder.json(build: JsonObjectBuilder.() -> Unit): Str
|
||||
}
|
||||
|
||||
@JsonBuilderDsl
|
||||
internal class JsonObjectBuilder(private val builder: StringBuilder) {
|
||||
internal class JsonObjectBuilder(private val builder: StringBuilder, private val isCompact: Boolean = false) {
|
||||
infix fun String.to(value: String) {
|
||||
builder
|
||||
.appendCommaIfNeed()
|
||||
.jsonEscapedString(this)
|
||||
.append(':')
|
||||
.jsonEscapedString(value)
|
||||
appendNameAndValue(this) {
|
||||
builder.jsonEscapedString(value)
|
||||
}
|
||||
}
|
||||
|
||||
infix fun String.toUnescaped(value: String) {
|
||||
builder
|
||||
.appendCommaIfNeed()
|
||||
.jsonEscapedString(this)
|
||||
.append(':')
|
||||
JsonUtil.escape(value, builder)
|
||||
appendNameAndValue(this) {
|
||||
JsonUtil.escape(value, builder)
|
||||
}
|
||||
}
|
||||
|
||||
infix fun String.to(value: Boolean) {
|
||||
builder
|
||||
.appendCommaIfNeed()
|
||||
.jsonEscapedString(this)
|
||||
.append(':')
|
||||
.append(value)
|
||||
appendNameAndValue(this) {
|
||||
builder.append(value)
|
||||
}
|
||||
}
|
||||
|
||||
infix fun String.to(value: StringBuilder) {
|
||||
@@ -45,34 +39,27 @@ internal class JsonObjectBuilder(private val builder: StringBuilder) {
|
||||
return
|
||||
}
|
||||
|
||||
builder
|
||||
.appendCommaIfNeed()
|
||||
.jsonEscapedString(this)
|
||||
.append(':')
|
||||
appendNameAndValue(this) {
|
||||
// append as is
|
||||
.append(value)
|
||||
builder.append(value)
|
||||
}
|
||||
}
|
||||
|
||||
infix fun String.toRaw(value: String) {
|
||||
builder
|
||||
.appendCommaIfNeed()
|
||||
.jsonEscapedString(this)
|
||||
.append(':')
|
||||
appendNameAndValue(this) {
|
||||
// append as is
|
||||
.append(value)
|
||||
builder.append(value)
|
||||
}
|
||||
}
|
||||
|
||||
fun map(key: CharSequence, build: JsonObjectBuilder.() -> Unit) {
|
||||
builder
|
||||
.appendCommaIfNeed()
|
||||
.jsonEscapedString(key)
|
||||
.append(':')
|
||||
.append('{')
|
||||
.append('\n')
|
||||
this.build()
|
||||
builder
|
||||
.append('\n')
|
||||
.append('}')
|
||||
|
||||
appendComplexValue('{', '}') {
|
||||
build()
|
||||
}
|
||||
}
|
||||
|
||||
fun rawMap(key: CharSequence, build: (StringBuilder) -> Unit) {
|
||||
@@ -87,37 +74,67 @@ internal class JsonObjectBuilder(private val builder: StringBuilder) {
|
||||
builder
|
||||
.appendCommaIfNeed()
|
||||
.jsonEscapedString(key)
|
||||
.append(':')
|
||||
.append(openChar)
|
||||
.append('\n')
|
||||
build(builder)
|
||||
builder
|
||||
.append('\n')
|
||||
.append(closeChar)
|
||||
appendComplexValue(openChar, closeChar) {
|
||||
build(builder)
|
||||
}
|
||||
}
|
||||
|
||||
fun rawBuilder(key: CharSequence, child: JsonObjectBuilder) {
|
||||
builder
|
||||
.appendCommaIfNeed()
|
||||
.jsonEscapedString(key)
|
||||
.append(':')
|
||||
.append('{')
|
||||
.append('\n')
|
||||
.append(child.builder)
|
||||
.append('\n')
|
||||
.append('}')
|
||||
appendComplexValue('{', '}') {
|
||||
builder.append(child.builder)
|
||||
}
|
||||
}
|
||||
|
||||
fun definitionReference(prefix: String, pointer: CharSequence) {
|
||||
builder
|
||||
.appendCommaIfNeed()
|
||||
.jsonEscapedString("\$ref")
|
||||
.append(':')
|
||||
.append('"')
|
||||
fun definitionReference(prefix: String, pointer: CharSequence, wrappingKey: String? = null) {
|
||||
builder.appendCommaIfNeed()
|
||||
|
||||
if (wrappingKey != null) {
|
||||
builder.append('"').append(wrappingKey).append('"').append(':').append(' ')
|
||||
}
|
||||
|
||||
builder.jsonEscapedString("\$ref")
|
||||
appendColon()
|
||||
builder.append('"')
|
||||
.append(prefix)
|
||||
.append(pointer)
|
||||
.append('"')
|
||||
}
|
||||
|
||||
private fun appendColon() {
|
||||
builder.append(':')
|
||||
if (!isCompact) {
|
||||
builder.append(' ')
|
||||
}
|
||||
}
|
||||
|
||||
private inline fun appendNameAndValue(name: CharSequence, valueAppender: () -> Unit) {
|
||||
builder
|
||||
.appendCommaIfNeed()
|
||||
.jsonEscapedString(name)
|
||||
appendColon()
|
||||
valueAppender()
|
||||
}
|
||||
|
||||
private inline fun appendComplexValue(openChar: Char, closeChar: Char, valueAppender: () -> Unit) {
|
||||
appendColon()
|
||||
builder.append(openChar)
|
||||
if (!isCompact) {
|
||||
builder
|
||||
.append('\n')
|
||||
.append(' ')
|
||||
.append(' ')
|
||||
}
|
||||
|
||||
valueAppender()
|
||||
|
||||
if (!isCompact) {
|
||||
builder.append('\n')
|
||||
}
|
||||
builder.append(closeChar)
|
||||
}
|
||||
}
|
||||
|
||||
private fun StringBuilder.appendCommaIfNeed(): StringBuilder {
|
||||
|
||||
@@ -9,8 +9,7 @@ import com.intellij.openapi.util.text.StringUtil
|
||||
import com.intellij.util.ReflectionUtil
|
||||
|
||||
internal inline fun processConfigurationTypes(processor: (configurationType: ConfigurationType, propertyName: CharSequence, factories: Array<ConfigurationFactory>) -> Unit) {
|
||||
val configurationTypes = ConfigurationType.CONFIGURATION_TYPE_EP.extensionList
|
||||
for (type in configurationTypes) {
|
||||
for (type in ConfigurationType.CONFIGURATION_TYPE_EP.extensionList) {
|
||||
val propertyName = rcTypeIdToPropertyName(type) ?: continue
|
||||
val factories = type.configurationFactories
|
||||
if (factories.isEmpty()) {
|
||||
@@ -27,7 +26,7 @@ private inline fun processFactories(factories: Array<ConfigurationFactory>,
|
||||
processor: (factoryPropertyName: CharSequence, factoryDefinitionId: CharSequence, factory: ConfigurationFactory) -> Unit) {
|
||||
for (factory in factories) {
|
||||
val factoryPropertyName = rcFactoryIdToPropertyName(factory) ?: continue
|
||||
val factoryDefinitionId = "${typeDefinitionId}-${factoryPropertyName}"
|
||||
val factoryDefinitionId = "${typeDefinitionId}-${factoryPropertyName}Factory"
|
||||
processor(factoryPropertyName, factoryDefinitionId, factory)
|
||||
}
|
||||
}
|
||||
@@ -44,18 +43,22 @@ internal class RunConfigurationJsonSchemaGenerator {
|
||||
addTemplatesNode(properties)
|
||||
|
||||
processConfigurationTypes { type, typePropertyName, factories ->
|
||||
@Suppress("UnnecessaryVariable")
|
||||
val typeDefinitionId = typePropertyName
|
||||
|
||||
val isMultiFactory = factories.size > 1
|
||||
addPropertyForConfigurationType(properties, typePropertyName, isMultiFactory, typeDefinitionId)
|
||||
val typeDefinitionId = generateTypeDefinitionId(typePropertyName)
|
||||
val typeDescription = getTypeDescription(type, typePropertyName)
|
||||
|
||||
addPropertyForConfigurationType(properties, typePropertyName, isMultiFactory, typeDefinitionId, typeDescription)
|
||||
|
||||
if (isMultiFactory) {
|
||||
processFactories(factories, typeDefinitionId) { factoryPropertyName, factoryDefinitionId, factory ->
|
||||
describeFactory(factory, factoryDefinitionId, if (StringUtil.equals(factoryPropertyName, factory.name)) null else factory.name)
|
||||
}
|
||||
|
||||
definitions.map(typeDefinitionId) {
|
||||
"type" to "object"
|
||||
|
||||
if (!StringUtil.equals(typePropertyName, type.configurationTypeDescription)) {
|
||||
"description" toUnescaped type.configurationTypeDescription
|
||||
if (typeDescription != null) {
|
||||
"description" toUnescaped typeDescription
|
||||
}
|
||||
|
||||
map("properties") {
|
||||
@@ -64,15 +67,12 @@ internal class RunConfigurationJsonSchemaGenerator {
|
||||
// describeFactory cannot be here because JsonBuilder instance here equals to definitions - recursive building is not supported (to reuse StringBuilder instance)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
processFactories(factories, typeDefinitionId) { factoryPropertyName, factoryDefinitionId, factory ->
|
||||
describeFactory(factory, factoryDefinitionId, if (StringUtil.equals(factoryPropertyName, factory.name)) null else factory.name)
|
||||
"additionalProperties" to false
|
||||
}
|
||||
}
|
||||
else {
|
||||
val description = type.configurationTypeDescription
|
||||
describeFactory(factories.first(), typeDefinitionId, if (typePropertyName == description) null else description)
|
||||
describeFactory(factories.first(), typeDefinitionId, typeDescription)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,20 +97,22 @@ internal class RunConfigurationJsonSchemaGenerator {
|
||||
"description" toUnescaped description
|
||||
map("properties") {
|
||||
processConfigurationTypes { type, typePropertyName, factories ->
|
||||
val typeDefinitionId = generateTypeDefinitionId(typePropertyName)
|
||||
val typeDescription = getTypeDescription(type, typePropertyName)
|
||||
if (factories.size == 1) {
|
||||
addPropertyForConfigurationType(this, typePropertyName, true, typePropertyName)
|
||||
addPropertyForConfigurationType(this, typePropertyName, true, typeDefinitionId, typeDescription)
|
||||
}
|
||||
else {
|
||||
// for multi-factory RC type we cannot simply reference to definition because the only child is expected (RC type cannot have more than one template)
|
||||
map(typePropertyName) {
|
||||
"type" to "object"
|
||||
|
||||
if (!StringUtil.equals(typePropertyName, type.configurationTypeDescription)) {
|
||||
"description" toUnescaped type.configurationTypeDescription
|
||||
if (typeDescription != null) {
|
||||
"description" toUnescaped typeDescription
|
||||
}
|
||||
|
||||
map("properties") {
|
||||
processFactories(factories, typePropertyName) { factoryPropertyName, factoryDefinitionId, _ ->
|
||||
processFactories(factories, typeDefinitionId) { factoryPropertyName, factoryDefinitionId, _ ->
|
||||
addPropertyForFactory(factoryPropertyName, factoryDefinitionId, isSingleChildOnly = true)
|
||||
}
|
||||
}
|
||||
@@ -121,22 +123,26 @@ internal class RunConfigurationJsonSchemaGenerator {
|
||||
}
|
||||
}
|
||||
|
||||
private fun addPropertyForConfigurationType(properties: JsonObjectBuilder, typePropertyName: CharSequence, isSingleChildOnly: Boolean, definitionId: CharSequence) {
|
||||
private fun addPropertyForConfigurationType(properties: JsonObjectBuilder, typePropertyName: CharSequence, isSingleChildOnly: Boolean, definitionId: CharSequence, typeDescription: String?) {
|
||||
properties.map(typePropertyName) {
|
||||
if (isSingleChildOnly) {
|
||||
"type" to "object"
|
||||
definitionReference(definitionPointerPrefix, definitionId)
|
||||
}
|
||||
else {
|
||||
"type" toRaw """["array", "object"]"""
|
||||
}
|
||||
|
||||
if (!isSingleChildOnly) {
|
||||
map("items") {
|
||||
definitionReference(definitionPointerPrefix, definitionId)
|
||||
rawArray("oneOf") {
|
||||
it.json {
|
||||
definitionReference(definitionPointerPrefix, definitionId)
|
||||
}
|
||||
it.json {
|
||||
"type" to "array"
|
||||
map("items") {
|
||||
definitionReference(definitionPointerPrefix, definitionId)
|
||||
}
|
||||
"additionalProperties" to false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
definitionReference(definitionPointerPrefix, definitionId)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -299,4 +305,13 @@ private fun idToPropertyName(string: String, configurationType: ConfigurationTyp
|
||||
else {
|
||||
return builder ?: result
|
||||
}
|
||||
}
|
||||
|
||||
private fun generateTypeDefinitionId(propertyName: CharSequence): String {
|
||||
return "${propertyName[0].toUpperCase()}${propertyName.substring(1)}Type"
|
||||
}
|
||||
|
||||
private fun getTypeDescription(type: ConfigurationType, typePropertyName: CharSequence): String? {
|
||||
val description = type.configurationTypeDescription
|
||||
return if (StringUtil.equals(typePropertyName, description)) null else description
|
||||
}
|
||||
@@ -33,6 +33,20 @@ internal class ConfigurationSchemaTest : CompletionTestCase() {
|
||||
checkDescription(variants, "isShowConsoleOnStdOut", "Show console when a message is printed to standard output stream")
|
||||
}
|
||||
|
||||
fun `test array or object`() {
|
||||
val variants = test("""
|
||||
runConfigurations:
|
||||
java: <caret>
|
||||
""".trimIndent())
|
||||
|
||||
val texts = variants.map {
|
||||
val presentation = LookupElementPresentation()
|
||||
it.renderElement(presentation)
|
||||
presentation.itemText
|
||||
}
|
||||
assertThat(texts).contains("{...}", "[...]")
|
||||
}
|
||||
|
||||
fun `test no isAllowRunningInParallel if singleton policy not configurable`() {
|
||||
val variants = test("""
|
||||
runConfigurations:
|
||||
@@ -56,6 +70,7 @@ internal class ConfigurationSchemaTest : CompletionTestCase() {
|
||||
val position = EditorTestUtil.getCaretPosition(text)
|
||||
assertThat(position).isGreaterThan(0)
|
||||
|
||||
@Suppress("SpellCheckingInspection")
|
||||
val file = createFile(myModule, "intellij.yaml", text.replace("<caret>", "IntelliJIDEARulezzz"))
|
||||
val element = file.findElementAt(position)
|
||||
assertThat(element).isNotNull
|
||||
|
||||
Reference in New Issue
Block a user