diff --git a/.github/workflows/mps-compatibility.yaml b/.github/workflows/mps-compatibility.yaml index b1dad40cb3..25a4097f25 100644 --- a/.github/workflows/mps-compatibility.yaml +++ b/.github/workflows/mps-compatibility.yaml @@ -22,6 +22,7 @@ jobs: - "2021.3.3" - "2022.2" - "2022.3" + - "2023.2" steps: - uses: actions/checkout@v4 diff --git a/build.gradle.kts b/build.gradle.kts index 83fc7868bb..a0e301e324 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -40,7 +40,6 @@ plugins { alias(libs.plugins.kotlin.multiplatform) apply false alias(libs.plugins.kotlin.serialization) apply false alias(libs.plugins.gitVersion) - alias(libs.plugins.ktlint) apply false alias(libs.plugins.spotless) apply false alias(libs.plugins.tasktree) alias(libs.plugins.dokka) @@ -78,7 +77,6 @@ subprojects { val subproject = this apply(plugin = "maven-publish") apply(plugin = "org.jetbrains.dokka") - apply(plugin = "org.jlleitschuh.gradle.ktlint") apply(plugin = "io.gitlab.arturbosch.detekt") version = rootProject.version @@ -90,11 +88,6 @@ subprojects { } } - configure { - // IMPORTANT: keep in sync with the version in .pre-commit-config.yaml - version.set("0.50.0") - } - tasks.withType { parallel = true // For now, we only use the results here as hints diff --git a/bulk-model-sync-gradle-test/graph-lang-api/build.gradle.kts b/bulk-model-sync-gradle-test/graph-lang-api/build.gradle.kts index 120e9622a8..310d746e02 100644 --- a/bulk-model-sync-gradle-test/graph-lang-api/build.gradle.kts +++ b/bulk-model-sync-gradle-test/graph-lang-api/build.gradle.kts @@ -36,6 +36,7 @@ kotlin { sourceSets.named("main") { kotlin.srcDir(kotlinGenDir) } + jvmToolchain(11) } metamodel { diff --git a/bulk-model-sync-gradle-test/src/test/kotlin/org/modelix/model/sync/bulk/gradle/test/PushTest.kt b/bulk-model-sync-gradle-test/src/test/kotlin/org/modelix/model/sync/bulk/gradle/test/PushTest.kt index ea9063ba64..1114dd1a6b 100644 --- a/bulk-model-sync-gradle-test/src/test/kotlin/org/modelix/model/sync/bulk/gradle/test/PushTest.kt +++ b/bulk-model-sync-gradle-test/src/test/kotlin/org/modelix/model/sync/bulk/gradle/test/PushTest.kt @@ -70,6 +70,6 @@ class PushTest { ?.getDescendants(false) ?.find { it.getConceptReference() == ConceptReference(_C_UntypedImpl_Graph.getUID()) }, ) - assertEquals(solution1Graph, solution2Graph.getReferenceTarget(C_Graph.relatedGraph)) + assertEquals(solution1Graph, solution2Graph.getReferenceTarget(C_Graph.relatedGraph.untyped())) } } diff --git a/model-api-gen-gradle/src/main/kotlin/org/modelix/metamodel/gradle/GenerateAntScriptForMpsMetaModelExport.kt b/model-api-gen-gradle/src/main/kotlin/org/modelix/metamodel/gradle/GenerateAntScriptForMpsMetaModelExport.kt index a21fbf12a9..168d73a2cc 100644 --- a/model-api-gen-gradle/src/main/kotlin/org/modelix/metamodel/gradle/GenerateAntScriptForMpsMetaModelExport.kt +++ b/model-api-gen-gradle/src/main/kotlin/org/modelix/metamodel/gradle/GenerateAntScriptForMpsMetaModelExport.kt @@ -91,6 +91,41 @@ abstract class GenerateAntScriptForMpsMetaModelExport @Inject constructor(of: Ob + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ${ if (exportModulesFilter.isPresent) { """""" diff --git a/model-api-gen-runtime/src/commonMain/kotlin/org/modelix/metamodel/GeneratedConcept.kt b/model-api-gen-runtime/src/commonMain/kotlin/org/modelix/metamodel/GeneratedConcept.kt index b573eecc28..ea2b641c99 100644 --- a/model-api-gen-runtime/src/commonMain/kotlin/org/modelix/metamodel/GeneratedConcept.kt +++ b/model-api-gen-runtime/src/commonMain/kotlin/org/modelix/metamodel/GeneratedConcept.kt @@ -231,7 +231,10 @@ abstract class GeneratedChildLink +fun IChildLink.typed(): ITypedChildLink { + return this as? ITypedChildLink + ?: if (isMultiple) UnknownTypedChildLinkList(this) else UnknownTypedSingleChildLink(this) +} open class GeneratedSingleChildLink>( owner: IConcept, @@ -281,4 +284,4 @@ class GeneratedReferenceLink +fun IReferenceLink.typed() = this as? ITypedReferenceLink ?: UnknownTypedReferenceLink(this) diff --git a/model-api-gen-runtime/src/commonMain/kotlin/org/modelix/metamodel/ITypedChildLink.kt b/model-api-gen-runtime/src/commonMain/kotlin/org/modelix/metamodel/ITypedChildLink.kt index 8668fcd57b..859d62e1a3 100644 --- a/model-api-gen-runtime/src/commonMain/kotlin/org/modelix/metamodel/ITypedChildLink.kt +++ b/model-api-gen-runtime/src/commonMain/kotlin/org/modelix/metamodel/ITypedChildLink.kt @@ -17,7 +17,7 @@ import org.modelix.model.api.IChildLink import org.modelix.model.api.INode import org.modelix.model.api.remove -interface ITypedChildLink : ITypedConceptFeature { +interface ITypedChildLink : ITypedConceptFeature { fun untyped(): IChildLink fun castChild(childNode: INode): ChildT fun getTypedChildConcept(): IConceptOfTypedNode diff --git a/model-api-gen-runtime/src/commonMain/kotlin/org/modelix/metamodel/ITypedReferenceLink.kt b/model-api-gen-runtime/src/commonMain/kotlin/org/modelix/metamodel/ITypedReferenceLink.kt index 0184c07b1e..99d75e73e6 100644 --- a/model-api-gen-runtime/src/commonMain/kotlin/org/modelix/metamodel/ITypedReferenceLink.kt +++ b/model-api-gen-runtime/src/commonMain/kotlin/org/modelix/metamodel/ITypedReferenceLink.kt @@ -16,7 +16,7 @@ package org.modelix.metamodel import org.modelix.model.api.INode import org.modelix.model.api.IReferenceLink -interface ITypedReferenceLink : ITypedConceptFeature { +interface ITypedReferenceLink : ITypedConceptFeature { fun untyped(): IReferenceLink fun castTarget(target: INode): TargetT fun getTypedTargetConcept(): IConceptOfTypedNode diff --git a/model-api-gen-runtime/src/commonMain/kotlin/org/modelix/metamodel/TypedLanguagesRegistry.kt b/model-api-gen-runtime/src/commonMain/kotlin/org/modelix/metamodel/TypedLanguagesRegistry.kt index d4be88ea9e..bc3f6258cf 100644 --- a/model-api-gen-runtime/src/commonMain/kotlin/org/modelix/metamodel/TypedLanguagesRegistry.kt +++ b/model-api-gen-runtime/src/commonMain/kotlin/org/modelix/metamodel/TypedLanguagesRegistry.kt @@ -49,7 +49,7 @@ object TypedLanguagesRegistry : ILanguageRepository { override fun getPriority(): Int = 2000 } -fun INode.typed(nodeClass: KClass): NodeT = nodeClass.cast(TypedLanguagesRegistry.wrapNode(this)) +fun INode.typed(nodeClass: KClass): NodeT = nodeClass.cast(TypedLanguagesRegistry.wrapNode(this)) inline fun INode.typed(): NodeT = TypedLanguagesRegistry.wrapNode(this) as NodeT fun INode.typedUnsafe(): NodeT = TypedLanguagesRegistry.wrapNode(this) as NodeT diff --git a/model-api-gen-runtime/src/commonMain/kotlin/org/modelix/metamodel/TypedNodeImpl.kt b/model-api-gen-runtime/src/commonMain/kotlin/org/modelix/metamodel/TypedNodeImpl.kt index 14cf5a1514..361fc5ca43 100644 --- a/model-api-gen-runtime/src/commonMain/kotlin/org/modelix/metamodel/TypedNodeImpl.kt +++ b/model-api-gen-runtime/src/commonMain/kotlin/org/modelix/metamodel/TypedNodeImpl.kt @@ -2,13 +2,12 @@ package org.modelix.metamodel import org.modelix.model.api.IConcept import org.modelix.model.api.INode -import org.modelix.model.api.getConcept abstract class TypedNodeImpl(val wrappedNode: INode) : ITypedNode { init { val expected: IConcept = _concept._concept - val actual: IConcept? = unwrap().getConcept() + val actual: IConcept? = unwrap().concept require(actual != null && actual.isSubConceptOf(expected)) { "Concept of node ${unwrap()} expected to be a sub-concept of $expected, but was $actual" } diff --git a/model-api-gen-runtime/src/commonMain/kotlin/org/modelix/metamodel/UnknownConcept.kt b/model-api-gen-runtime/src/commonMain/kotlin/org/modelix/metamodel/UnknownConcept.kt index daed393675..527b6b5b3c 100644 --- a/model-api-gen-runtime/src/commonMain/kotlin/org/modelix/metamodel/UnknownConcept.kt +++ b/model-api-gen-runtime/src/commonMain/kotlin/org/modelix/metamodel/UnknownConcept.kt @@ -8,6 +8,7 @@ import org.modelix.model.api.INode import org.modelix.model.api.IProperty import org.modelix.model.api.IReferenceLink import org.modelix.model.area.IArea +import kotlin.reflect.KClass abstract class EmptyConcept : IConcept { override fun isAbstract(): Boolean = true @@ -77,15 +78,52 @@ data class UnknownConcept(private val ref: IConceptReference) : EmptyConcept() { override fun getLongName(): String = getShortName() } -data class UnknownTypedConcept(private val ref: IConceptReference?) : ITypedConcept { +data class UnknownTypedConcept(private val ref: IConcept?) : IConceptOfTypedNode { override fun untyped(): IConcept { - return ref?.let { UnknownConcept(it) } ?: NullConcept + return ref ?: NullConcept + } + + override fun getInstanceInterface(): KClass { + return UnknownConceptInstance::class } } data class UnknownConceptInstance(val node: INode) : ITypedNode { override val _concept: ITypedConcept - get() = UnknownTypedConcept(node.getConceptReference()) + get() = UnknownTypedConcept(node.concept) override fun unwrap(): INode = node } + +abstract class UnknownTypedChildLink : ITypedChildLink { + + override fun castChild(childNode: INode): ITypedNode { + return childNode.typed() + } + + override fun getTypedChildConcept(): IConceptOfTypedNode { + return untyped().targetConcept.typed() as IConceptOfTypedNode + } +} + +data class UnknownTypedSingleChildLink(private val link: IChildLink) : UnknownTypedChildLink(), ITypedSingleChildLink { + override fun untyped(): IChildLink = link +} + +data class UnknownTypedChildLinkList(private val link: IChildLink) : UnknownTypedChildLink(), ITypedChildListLink { + override fun untyped(): IChildLink = link +} + +data class UnknownTypedReferenceLink(private val link: IReferenceLink) : ITypedReferenceLink { + override fun untyped(): IReferenceLink { + return link + } + + override fun castTarget(target: INode): ITypedNode { + return target.typed() + } + + override fun getTypedTargetConcept(): IConceptOfTypedNode { + return untyped().targetConcept.typed() as IConceptOfTypedNode + } +} diff --git a/model-api-gen/src/main/kotlin/org/modelix/metamodel/generator/internal/ConceptWrapperInterfaceGenerator.kt b/model-api-gen/src/main/kotlin/org/modelix/metamodel/generator/internal/ConceptWrapperInterfaceGenerator.kt index f9066016b3..5e4f0fc5e2 100644 --- a/model-api-gen/src/main/kotlin/org/modelix/metamodel/generator/internal/ConceptWrapperInterfaceGenerator.kt +++ b/model-api-gen/src/main/kotlin/org/modelix/metamodel/generator/internal/ConceptWrapperInterfaceGenerator.kt @@ -23,9 +23,7 @@ import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy import com.squareup.kotlinpoet.PropertySpec import com.squareup.kotlinpoet.TypeSpec import com.squareup.kotlinpoet.TypeVariableName -import com.squareup.kotlinpoet.asClassName import com.squareup.kotlinpoet.asTypeName -import org.modelix.metamodel.GeneratedProperty import org.modelix.metamodel.IConceptOfTypedNode import org.modelix.metamodel.INonAbstractConcept import org.modelix.metamodel.ITypedConcept @@ -91,6 +89,7 @@ internal class ConceptWrapperInterfaceGenerator( private fun TypeSpec.Builder.addConceptMetaPropertiesIfNecessary() { if (conceptPropertiesInterfaceName == null) return + // TODO use IConcept.getConceptProperty(name: String) concept.metaProperties.forEach { (key, value) -> val propertySpec = PropertySpec.builder(key, String::class.asTypeName()).runBuild { addModifiers(KModifier.OVERRIDE) @@ -102,7 +101,7 @@ internal class ConceptWrapperInterfaceGenerator( } private fun TypeSpec.Builder.addConceptWrapperInterfaceReferenceLink(referenceLink: ProcessedReferenceLink) { - val propertySpec = PropertySpec.builder(referenceLink.generatedName, referenceLink.generatedReferenceLinkType()).runBuild { + val propertySpec = PropertySpec.builder(referenceLink.generatedName, referenceLink.typedLinkType()).runBuild { getter(FunSpec.getterBuilder().addCode(referenceLink.returnKotlinRef()).build()) addDeprecationIfNecessary(referenceLink) } @@ -111,7 +110,7 @@ internal class ConceptWrapperInterfaceGenerator( } private fun TypeSpec.Builder.addConceptWrapperInterfaceChildLink(childLink: ProcessedChildLink) { - val propertySpec = PropertySpec.builder(childLink.generatedName, childLink.generatedChildLinkType()).runBuild { + val propertySpec = PropertySpec.builder(childLink.generatedName, childLink.typedLinkType()).runBuild { getter(FunSpec.getterBuilder().addCode(childLink.returnKotlinRef()).build()) addDeprecationIfNecessary(childLink) } @@ -122,8 +121,7 @@ internal class ConceptWrapperInterfaceGenerator( private fun TypeSpec.Builder.addConceptWrapperInterfaceProperty(property: ProcessedProperty) { val propertySpec = PropertySpec.builder( name = property.generatedName, - type = GeneratedProperty::class.asClassName() - .parameterizedBy(property.asKotlinType(alwaysUseNonNullableProperties)), + type = property.typedPropertyType(alwaysUseNonNullableProperties), ).runBuild { val getterSpec = FunSpec.getterBuilder().runBuild { addCode(property.returnKotlinRef()) diff --git a/model-api-gen/src/main/kotlin/org/modelix/metamodel/generator/internal/NameConfigBasedGenerator.kt b/model-api-gen/src/main/kotlin/org/modelix/metamodel/generator/internal/NameConfigBasedGenerator.kt index 1b556928d7..7d83f1a859 100644 --- a/model-api-gen/src/main/kotlin/org/modelix/metamodel/generator/internal/NameConfigBasedGenerator.kt +++ b/model-api-gen/src/main/kotlin/org/modelix/metamodel/generator/internal/NameConfigBasedGenerator.kt @@ -26,6 +26,11 @@ import org.modelix.metamodel.GeneratedChildListLink import org.modelix.metamodel.GeneratedMandatorySingleChildLink import org.modelix.metamodel.GeneratedReferenceLink import org.modelix.metamodel.GeneratedSingleChildLink +import org.modelix.metamodel.ITypedChildListLink +import org.modelix.metamodel.ITypedMandatorySingleChildLink +import org.modelix.metamodel.ITypedProperty +import org.modelix.metamodel.ITypedReferenceLink +import org.modelix.metamodel.ITypedSingleChildLink import org.modelix.metamodel.generator.NameConfig import org.modelix.metamodel.generator.ProcessedChildLink import org.modelix.metamodel.generator.ProcessedConcept @@ -77,6 +82,18 @@ internal abstract class NameConfigBasedGenerator(open val nameConfig: NameConfig ) } + protected fun ProcessedChildLink.typedLinkType(): TypeName { + val childConcept = type.resolved + val linkClass = if (multiple) { + ITypedChildListLink::class + } else { + if (optional) ITypedSingleChildLink::class else ITypedMandatorySingleChildLink::class + } + return linkClass.asClassName().parameterizedBy( + childConcept.nodeWrapperInterfaceType(), + ) + } + protected fun ProcessedReferenceLink.generatedReferenceLinkType(): TypeName { val targetConcept = type.resolved return GeneratedReferenceLink::class.asClassName().parameterizedBy( @@ -85,6 +102,19 @@ internal abstract class NameConfigBasedGenerator(open val nameConfig: NameConfig ) } + protected fun ProcessedReferenceLink.typedLinkType(): TypeName { + val targetConcept = type.resolved + return ITypedReferenceLink::class.asClassName().parameterizedBy( + targetConcept.nodeWrapperInterfaceType(), + ) + } + + protected fun ProcessedProperty.typedPropertyType(alwaysUseNonNullableProperties: Boolean): TypeName { + return ITypedProperty::class.asClassName().parameterizedBy( + asKotlinType(alwaysUseNonNullableProperties), + ) + } + protected fun ProcessedProperty.asKotlinType(alwaysUseNonNullableProperties: Boolean): TypeName { val nonNullableType = when (type) { is PrimitivePropertyType -> when ((type as PrimitivePropertyType).primitive) { diff --git a/model-api/build.gradle.kts b/model-api/build.gradle.kts index 29c0d3f988..ce1f380e6a 100644 --- a/model-api/build.gradle.kts +++ b/model-api/build.gradle.kts @@ -6,16 +6,6 @@ plugins { description = "API to access models stored in Modelix" -ktlint { - filter { - exclude { - val kotlinGeneratedFromTypeScript = - project(":ts-model-api").layout.buildDirectory.get().asFile.toPath().toAbsolutePath() - it.file.toPath().toAbsolutePath().startsWith(kotlinGeneratedFromTypeScript) - } - } -} - kotlin { jvm() js(IR) { @@ -70,7 +60,6 @@ kotlin { listOf( "sourcesJar", - "runKtlintCheckOverJsMainSourceSet", "jsSourcesJar", "jsPackageJson", "compileKotlinJs", diff --git a/model-api/src/commonMain/kotlin/org/modelix/model/api/IConcept.kt b/model-api/src/commonMain/kotlin/org/modelix/model/api/IConcept.kt index 42cc51837d..2c3ff5d017 100644 --- a/model-api/src/commonMain/kotlin/org/modelix/model/api/IConcept.kt +++ b/model-api/src/commonMain/kotlin/org/modelix/model/api/IConcept.kt @@ -168,9 +168,16 @@ interface IConcept { * @return reference link */ fun getReferenceLink(name: String): IReferenceLink + + /** + * The alias of an MPS concept is one example of a concept property. + */ + fun getConceptProperty(name: String): String? = null } /** * @see IConcept.isSubConceptOf */ fun IConcept?.isSubConceptOf(superConcept: IConcept?) = this?.isSubConceptOf(superConcept) == true + +fun IConcept.conceptAlias() = getConceptProperty("alias") diff --git a/model-api/src/commonMain/kotlin/org/modelix/model/api/INode.kt b/model-api/src/commonMain/kotlin/org/modelix/model/api/INode.kt index 4233949423..670d77ef2e 100644 --- a/model-api/src/commonMain/kotlin/org/modelix/model/api/INode.kt +++ b/model-api/src/commonMain/kotlin/org/modelix/model/api/INode.kt @@ -307,6 +307,7 @@ fun INode.setReferenceTarget(link: IReferenceLink, target: INodeReference?): Uni fun INode.getPropertyValue(property: IProperty): String? = if (this is INodeEx) getPropertyValue(property) else getPropertyValue(property.key(this)) fun INode.setPropertyValue(property: IProperty, value: String?): Unit = if (this is INodeEx) setPropertyValue(property, value) else setPropertyValue(property.key(this), value) +@Deprecated("use INode.concept", ReplaceWith("concept")) fun INode.getConcept(): IConcept? = getConceptReference()?.resolve() fun INode.getResolvedReferenceTarget(role: String): INode? = getReferenceTargetRef(role)?.resolveIn(getArea()!!) fun INode.getResolvedConcept(): IConcept? = getConceptReference()?.resolve() diff --git a/model-api/src/commonMain/kotlin/org/modelix/model/api/ITransactionManager.kt b/model-api/src/commonMain/kotlin/org/modelix/model/api/ITransactionManager.kt new file mode 100644 index 0000000000..480aaad2c8 --- /dev/null +++ b/model-api/src/commonMain/kotlin/org/modelix/model/api/ITransactionManager.kt @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2023. + * + * 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 org.modelix.model.api + +interface ITransactionManager { + fun executeRead(body: () -> R): R + fun executeWrite(body: () -> R): R + fun canRead(): Boolean + fun canWrite(): Boolean +} diff --git a/model-api/src/commonMain/kotlin/org/modelix/model/api/SerializedNodeReference.kt b/model-api/src/commonMain/kotlin/org/modelix/model/api/SerializedNodeReference.kt index e372970ca0..3d968bcf50 100644 --- a/model-api/src/commonMain/kotlin/org/modelix/model/api/SerializedNodeReference.kt +++ b/model-api/src/commonMain/kotlin/org/modelix/model/api/SerializedNodeReference.kt @@ -1,8 +1,11 @@ package org.modelix.model.api +import kotlinx.serialization.Serializable + @Deprecated("renamed to NodeReference", ReplaceWith("NodeReference")) typealias SerializedNodeReference = NodeReference +@Serializable data class NodeReference(val serialized: String) : INodeReference { override fun serialize(): String = serialized } diff --git a/model-api/src/commonMain/kotlin/org/modelix/model/area/IArea.kt b/model-api/src/commonMain/kotlin/org/modelix/model/area/IArea.kt index f9097b52a9..63f188bdb8 100644 --- a/model-api/src/commonMain/kotlin/org/modelix/model/area/IArea.kt +++ b/model-api/src/commonMain/kotlin/org/modelix/model/area/IArea.kt @@ -19,6 +19,7 @@ import org.modelix.model.api.IConceptReference import org.modelix.model.api.INode import org.modelix.model.api.INodeReference import org.modelix.model.api.INodeResolutionScope +import org.modelix.model.api.ITransactionManager /** * An IArea is similar to an IBranch. They both provide transactional access to nodes, but the IBranch can only be used @@ -27,7 +28,7 @@ import org.modelix.model.api.INodeResolutionScope * It's like a unix filesystem with mount points. The model inside an area can also be an MPS model that is not a * persistent data structure. */ -interface IArea : INodeResolutionScope { +interface IArea : INodeResolutionScope, ITransactionManager { /** * The root of an area is not allowed to change */ @@ -53,10 +54,10 @@ interface IArea : INodeResolutionScope { fun getReference(): IAreaReference fun resolveArea(ref: IAreaReference): IArea? - fun executeRead(f: () -> T): T - fun executeWrite(f: () -> T): T - fun canRead(): Boolean - fun canWrite(): Boolean + override fun executeRead(body: () -> T): T + override fun executeWrite(body: () -> T): T + override fun canRead(): Boolean + override fun canWrite(): Boolean /** bigger numbers are locked first */ fun getLockOrderingPriority(): Long = 0 diff --git a/model-server/build.gradle.kts b/model-server/build.gradle.kts index f614704c2c..7721677ee2 100644 --- a/model-server/build.gradle.kts +++ b/model-server/build.gradle.kts @@ -130,10 +130,6 @@ tasks.register("copyApis") { sourceSets["main"].resources.srcDir(project.layout.buildDirectory.dir("openapi/src/main/resources/")) } -tasks.named("runKtlintCheckOverMainSourceSet") { - dependsOn("copyApis") -} - tasks.named("compileKotlin") { dependsOn("copyApis") } @@ -282,18 +278,6 @@ openApiFiles.forEach { tasks.named("compileKotlin") { dependsOn(targetTaskName) } - tasks.named("runKtlintCheckOverMainSourceSet") { - dependsOn(targetTaskName) - } - - // do not apply ktlint on the generated files - ktlint { - filter { - exclude { - it.file.toPath().toAbsolutePath().startsWith(outputPath) - } - } - } // add openAPI generated artifacts to the sourceSets sourceSets["main"].kotlin.srcDir("$outputPath/src/main/kotlin") diff --git a/mps-model-adapters/build.gradle.kts b/mps-model-adapters/build.gradle.kts index a765db517f..0c8ad481e0 100644 --- a/mps-model-adapters/build.gradle.kts +++ b/mps-model-adapters/build.gradle.kts @@ -7,23 +7,32 @@ val mpsVersion = project.findProperty("mps.version")?.toString().takeIf { !it.is dependencies { api(project(":model-api")) + implementation(libs.modelix.incremental) - compileOnly("com.jetbrains:mps-openapi:$mpsVersion") - compileOnly("com.jetbrains:mps-core:$mpsVersion") - compileOnly("com.jetbrains:mps-environment:$mpsVersion") - implementation(libs.trove) + val mpsZip by configurations.creating + mpsZip("com.jetbrains:mps:$mpsVersion") + compileOnly( + zipTree({ mpsZip.singleFile }).matching { + include("lib/*.jar") + }, + ) + implementation(libs.trove) implementation(kotlin("stdlib")) implementation(libs.kotlin.logging) } group = "org.modelix.mps" +java { + withSourcesJar() +} + publishing { publications { create("maven") { artifactId = "model-adapters" - from(components["kotlin"]) + from(components["java"]) } } } diff --git a/mps-model-adapters/src/main/kotlin/org/modelix/model/mpsadapters/GlobalModelListener.kt b/mps-model-adapters/src/main/kotlin/org/modelix/model/mpsadapters/GlobalModelListener.kt new file mode 100644 index 0000000000..74e469ac83 --- /dev/null +++ b/mps-model-adapters/src/main/kotlin/org/modelix/model/mpsadapters/GlobalModelListener.kt @@ -0,0 +1,130 @@ +@file:Suppress("removal") + +/* + * Copyright (c) 2023. + * + * 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 org.modelix.model.mpsadapters + +import org.jetbrains.mps.openapi.model.SModel +import org.jetbrains.mps.openapi.module.SModule +import org.jetbrains.mps.openapi.module.SModuleListener +import org.jetbrains.mps.openapi.module.SModuleListenerBase +import org.jetbrains.mps.openapi.module.SRepository +import org.jetbrains.mps.openapi.module.SRepositoryListener +import org.jetbrains.mps.openapi.module.SRepositoryListenerBase + +abstract class GlobalModelListener { + protected var repositoryListener: SRepositoryListener = object : SRepositoryListenerBase() { + override fun moduleAdded(m: SModule) { + start(m) + } + + override fun beforeModuleRemoved(m: SModule) { + stop(m) + } + } + + protected var moduleListener: SModuleListener = object : SModuleListenerBase() { + override fun modelAdded(module: SModule, model: SModel) { + start(model) + } + + override fun beforeModelRemoved(module: SModule, model: SModel) { + stop(model) + } + } + protected var myRepositories: MutableSet = HashSet() + protected var myModules: MutableSet = HashSet() + protected var myModels: MutableSet = HashSet() + fun start(repo: SRepository) { + if (myRepositories.contains(repo)) { + return + } + myRepositories.add(repo) + repo.addRepositoryListener(repositoryListener) + repo.modelAccess.runReadAction { + for (module in repo.modules) { + start(module) + } + } + addListener(repo) + } + + open fun start(module: SModule) { + if (myModules.contains(module)) { + return + } + myModules.add(module) + module.addModuleListener(moduleListener) + for (model in module.models) { + start(model) + } + addListener(module) + } + + fun start(model: SModel) { + if (myModels.contains(model)) { + return + } + myModels.add(model) + addListener(model) + } + + protected open fun addListener(repository: SRepository) {} + protected open fun addListener(module: SModule) {} + protected abstract fun addListener(model: SModel) + fun stop() { + for (repo in myRepositories) { + repo.modelAccess.runReadAction { stop(repo) } + } + } + + fun stop(repo: SRepository) { + if (!myRepositories.contains(repo)) { + return + } + myRepositories.remove(repo) + repo.removeRepositoryListener(repositoryListener) + for (module in repo.modules) { + stop(module) + } + removeListener(repo) + } + + open fun stop(module: SModule) { + if (!myModules.contains(module)) { + return + } + myModules.remove(module) + module.removeModuleListener(moduleListener) + for (model in module.models) { + stop(model) + } + removeListener(module) + } + + fun stop(model: SModel) { + if (!myModels.contains(model)) { + return + } + myModels.remove(model) + removeListener(model) + } + + protected open fun removeListener(repository: SRepository) {} + protected open fun removeListener(module: SModule) {} + protected abstract fun removeListener(model: SModel) +} diff --git a/mps-model-adapters/src/main/kotlin/org/modelix/model/mpsadapters/MPSArea.kt b/mps-model-adapters/src/main/kotlin/org/modelix/model/mpsadapters/MPSArea.kt index acbc27c225..cace44d610 100644 --- a/mps-model-adapters/src/main/kotlin/org/modelix/model/mpsadapters/MPSArea.kt +++ b/mps-model-adapters/src/main/kotlin/org/modelix/model/mpsadapters/MPSArea.kt @@ -13,6 +13,8 @@ */ package org.modelix.model.mpsadapters +import jetbrains.mps.ide.ThreadUtils +import jetbrains.mps.project.Project import jetbrains.mps.project.ProjectBase import jetbrains.mps.project.ProjectManager import jetbrains.mps.project.facets.JavaModuleFacet @@ -108,12 +110,42 @@ data class MPSArea(val repository: SRepository) : IArea, IAreaReference { override fun executeWrite(f: () -> T): T { var result: T? = null - if (repository.modelAccess is GlobalModelAccess) { - repository.modelAccess.runWriteAction { result = f() } - } else { - repository.modelAccess.executeCommand { result = f() } + executeWrite({ result = f() }, enforceCommand = true) + return result as T + } + + fun executeWrite(f: () -> Unit, enforceCommand: Boolean) { + // Try to execute a command instead of a write action if possible, + // because write actions don't trigger an update of the MPS editor. + + // A command can only be executed on the EDT (Event Dispatch Thread/AWT Thread/UI Thread). + // We could dispatch it to the EDT and wait for the result, but that increases the risk for deadlocks. + // The caller is responsible for calling this method from the EDT if a command is desired. + val inEDT = ThreadUtils.isInEDT() + + if (inEDT || enforceCommand) { + val projects: Sequence = Sequence { ProjectManager.getInstance().openedProjects.iterator() } + val modelAccessCandidates = sequenceOf(repository.modelAccess) + projects.map { it.modelAccess } + // GlobalModelAccess throws an Exception when trying to execute a command. + // Only a ProjectModelAccess can execute a command. + val modelAccess = modelAccessCandidates.filter { it !is GlobalModelAccess }.firstOrNull() + + if (modelAccess != null) { + if (inEDT) { + modelAccess.executeCommand { f() } + } else { + ThreadUtils.runInUIThreadAndWait { + modelAccess.executeCommand { f() } + } + } + return + } } - return result!! + + // For a write access any ModelAccess works. + // If there is no ModelAccess that is not a GlobalModelAccess then there are probably no open projects and + // there can't be any open editors, so the issues doesn't exist. + repository.modelAccess.runWriteAction { f() } } override fun canRead(): Boolean { diff --git a/mps-model-adapters/src/main/kotlin/org/modelix/model/mpsadapters/MPSChangeTranslator.kt b/mps-model-adapters/src/main/kotlin/org/modelix/model/mpsadapters/MPSChangeTranslator.kt new file mode 100644 index 0000000000..eaccd6f424 --- /dev/null +++ b/mps-model-adapters/src/main/kotlin/org/modelix/model/mpsadapters/MPSChangeTranslator.kt @@ -0,0 +1,244 @@ +@file:Suppress("removal") + +package org.modelix.model.mpsadapters + +import jetbrains.mps.smodel.SModelInternal +import jetbrains.mps.smodel.event.SModelChildEvent +import jetbrains.mps.smodel.event.SModelDevKitEvent +import jetbrains.mps.smodel.event.SModelImportEvent +import jetbrains.mps.smodel.event.SModelLanguageEvent +import jetbrains.mps.smodel.event.SModelListener +import jetbrains.mps.smodel.event.SModelPropertyEvent +import jetbrains.mps.smodel.event.SModelReferenceEvent +import jetbrains.mps.smodel.event.SModelRenamedEvent +import jetbrains.mps.smodel.event.SModelRootEvent +import jetbrains.mps.smodel.loading.ModelLoadingState +import org.jetbrains.mps.openapi.event.SNodeAddEvent +import org.jetbrains.mps.openapi.event.SNodeRemoveEvent +import org.jetbrains.mps.openapi.event.SPropertyChangeEvent +import org.jetbrains.mps.openapi.event.SReferenceChangeEvent +import org.jetbrains.mps.openapi.language.SContainmentLink +import org.jetbrains.mps.openapi.language.SLanguage +import org.jetbrains.mps.openapi.language.SProperty +import org.jetbrains.mps.openapi.language.SReferenceLink +import org.jetbrains.mps.openapi.model.SModel +import org.jetbrains.mps.openapi.model.SModelReference +import org.jetbrains.mps.openapi.model.SNode +import org.jetbrains.mps.openapi.model.SNodeChangeListener +import org.jetbrains.mps.openapi.module.SDependency +import org.jetbrains.mps.openapi.module.SModule +import org.jetbrains.mps.openapi.module.SModuleListener +import org.jetbrains.mps.openapi.module.SModuleReference +import org.jetbrains.mps.openapi.module.SRepository +import org.jetbrains.mps.openapi.module.SRepositoryListener +import org.jetbrains.mps.openapi.module.SRepositoryListenerBase +import org.modelix.incremental.DependencyTracking +import org.modelix.incremental.IStateVariableReference + +class MPSChangeTranslator : + GlobalModelListener(), + SNodeChangeListener, + SModuleListener, + SRepositoryListener by object : SRepositoryListenerBase() {}, + SModelListener, + org.jetbrains.mps.openapi.model.SModelListener { + + private fun notifyChange(change: IStateVariableReference<*>) { + DependencyTracking.modified(change) + } + + override fun addListener(model: SModel) { + model.addChangeListener(this) + model.addModelListener(this) + (model as SModelInternal).addModelListener(this) + } + + override fun removeListener(model: SModel) { + model.removeChangeListener(this) + model.removeModelListener(this) + (model as SModelInternal).removeModelListener(this) + } + + override fun addListener(module: SModule) { + module.addModuleListener(this) + } + + override fun removeListener(module: SModule) { + module.removeModuleListener(this) + } + + override fun addListener(repository: SRepository) { + repository.addRepositoryListener(this) + } + + override fun removeListener(repository: SRepository) { + repository.removeRepositoryListener(this) + } + + override fun propertyChanged(e: SPropertyChangeEvent) { + notifyChange(MPSPropertyDependency(e.node, e.property)) + } + + override fun referenceChanged(e: SReferenceChangeEvent) { + notifyChange(MPSReferenceDependency(e.node, e.associationLink)) + } + + override fun nodeAdded(e: SNodeAddEvent) { + if (e.parent != null) { + notifyChange(MPSChildrenDependency(e.parent!!, e.aggregationLink!!)) + } else { + notifyChange(MPSRootNodesListDependency(e.model)) + } + notifyChange(MPSContainmentDependency(e.child)) + } + + override fun nodeRemoved(e: SNodeRemoveEvent) { + if (e.parent != null) { + notifyChange(MPSChildrenDependency(e.parent!!, e.aggregationLink!!)) + } else { + notifyChange(MPSRootNodesListDependency(e.model)) + } + notifyChange(MPSContainmentDependency(e.child)) + } + + override fun beforeChildRemoved(event: SModelChildEvent) {} + override fun beforeModelDisposed(model: SModel) {} + override fun beforeModelRenamed(event: SModelRenamedEvent) {} + override fun beforeRootRemoved(event: SModelRootEvent) {} + override fun childAdded(event: SModelChildEvent) {} + override fun childRemoved(event: SModelChildEvent) {} + override fun devkitAdded(event: SModelDevKitEvent) {} + override fun devkitRemoved(event: SModelDevKitEvent) {} + override fun getPriority(): SModelListener.SModelListenerPriority { + return SModelListener.SModelListenerPriority.CLIENT + } + + override fun importAdded(event: SModelImportEvent) { + notifyChange(MPSModelDependency(event.model)) + } + + override fun importRemoved(event: SModelImportEvent) { + notifyChange(MPSModelDependency(event.model)) + } + + override fun languageAdded(event: SModelLanguageEvent) {} + override fun languageRemoved(event: SModelLanguageEvent) {} + override fun modelLoadingStateChanged(model: SModel, state: ModelLoadingState) {} + override fun modelRenamed(event: SModelRenamedEvent) {} + override fun modelSaved(model: SModel) {} + override fun propertyChanged(event: SModelPropertyEvent) {} + override fun referenceAdded(event: SModelReferenceEvent) {} + override fun referenceRemoved(event: SModelReferenceEvent) {} + + @Deprecated("") + override fun rootAdded(event: SModelRootEvent) { + } + + @Deprecated("") + override fun rootRemoved(event: SModelRootEvent) { + } + + override fun modelLoaded(model: SModel, partially: Boolean) {} + override fun modelReplaced(model: SModel) { + notifyChange(MPSModelContentDependency(model)) + } + + override fun modelUnloaded(model: SModel) {} + override fun modelAttached(model: SModel, repository: SRepository) {} + override fun modelDetached(model: SModel, repository: SRepository) {} + override fun conflictDetected(model: SModel) {} + override fun problemsDetected(model: SModel, problems: Iterable) {} + override fun modelAdded(module: SModule, model: SModel) { + notifyChange(MPSModuleDependency(module)) + } + + override fun beforeModelRemoved(module: SModule, model: SModel) { + notifyChange(MPSModuleDependency(module)) + } + + override fun modelRemoved(module: SModule, reference: SModelReference) {} + override fun beforeModelRenamed(module: SModule, model: SModel, reference: SModelReference) {} + override fun modelRenamed(module: SModule, model: SModel, reference: SModelReference) { + notifyChange(MPSModelDependency(model)) + } + + override fun dependencyAdded(module: SModule, dependency: SDependency) {} + override fun dependencyRemoved(module: SModule, dependency: SDependency) {} + override fun languageAdded(module: SModule, language: SLanguage) {} + override fun languageRemoved(module: SModule, language: SLanguage) {} + override fun moduleChanged(module: SModule) {} + override fun moduleAdded(module: SModule) { + notifyChange(MPSRepositoryDependency) + } + + override fun beforeModuleRemoved(module: SModule) { + notifyChange(MPSRepositoryDependency) + } + + override fun moduleRemoved(reference: SModuleReference) {} + override fun commandStarted(repository: SRepository) {} + override fun commandFinished(repository: SRepository) {} +} + +abstract class MPSDependencyBase : IStateVariableReference { + override fun read(): Any? = null +} + +data class MPSNodeDependency(val node: SNode) : MPSDependencyBase() { + override fun getGroup() = node.parent?.let { MPSNodeDependency(it) } +} + +data class MPSPropertyDependency(val node: SNode, val property: SProperty) : MPSDependencyBase() { + override fun getGroup() = MPSAllPropertiesDependency(node) +} + +data class MPSReferenceDependency(val node: SNode, val link: SReferenceLink) : MPSDependencyBase() { + override fun getGroup() = MPSAllReferencesDependency(node) +} + +data class MPSChildrenDependency(val node: SNode, val link: SContainmentLink) : MPSDependencyBase() { + override fun getGroup() = MPSAllChildrenDependency(node) +} + +data class MPSAllChildrenDependency(val node: SNode) : MPSDependencyBase() { + override fun getGroup() = MPSNodeDependency(node) +} + +data class MPSAllPropertiesDependency(val node: SNode) : MPSDependencyBase() { + override fun getGroup() = MPSNodeDependency(node) +} + +data class MPSAllReferencesDependency(val node: SNode) : MPSDependencyBase() { + override fun getGroup() = MPSNodeDependency(node) +} + +data class MPSContainmentDependency(val node: SNode) : MPSDependencyBase() { + override fun getGroup() = MPSNodeDependency(node) +} + +/** + * No SRepository parameter, because there is only one repository in MPS. + * If one repository changes, all of them change. + */ +object MPSRepositoryDependency : MPSDependencyBase() { + override fun getGroup() = null +} + +data class MPSModuleDependency(val model: SModule) : MPSDependencyBase() { + override fun getGroup() = MPSRepositoryDependency +} + +data class MPSModelDependency(val model: SModel) : MPSDependencyBase() { + override fun getGroup() = MPSModuleDependency(model.module) +} + +data class MPSRootNodesListDependency(val model: SModel) : MPSDependencyBase() { + override fun getGroup() = MPSModelDependency(model) +} + +/** + * This is used to handle the case that MPS reloads a model from disk. + */ +data class MPSModelContentDependency(val model: SModel) : MPSDependencyBase() { + override fun getGroup() = MPSModelDependency(model) +} diff --git a/mps-model-adapters/src/main/kotlin/org/modelix/model/mpsadapters/MPSConcept.kt b/mps-model-adapters/src/main/kotlin/org/modelix/model/mpsadapters/MPSConcept.kt index 78a4fea1e7..d51c374c4a 100644 --- a/mps-model-adapters/src/main/kotlin/org/modelix/model/mpsadapters/MPSConcept.kt +++ b/mps-model-adapters/src/main/kotlin/org/modelix/model/mpsadapters/MPSConcept.kt @@ -111,4 +111,11 @@ data class MPSConcept(val concept: SAbstractConceptAdapter) : IConcept { override fun getReferenceLink(name: String): IReferenceLink { return MPSReferenceLink(concept.referenceLinks.first { it.name == name }) } + + override fun getConceptProperty(name: String): String? { + return when (name) { + "alias" -> concept.conceptAlias + else -> null + } + } } diff --git a/mps-model-adapters/src/main/kotlin/org/modelix/model/mpsadapters/MPSNode.kt b/mps-model-adapters/src/main/kotlin/org/modelix/model/mpsadapters/MPSNode.kt index 7b35173284..0338b48ae0 100644 --- a/mps-model-adapters/src/main/kotlin/org/modelix/model/mpsadapters/MPSNode.kt +++ b/mps-model-adapters/src/main/kotlin/org/modelix/model/mpsadapters/MPSNode.kt @@ -24,6 +24,7 @@ import jetbrains.mps.smodel.adapter.structure.property.SPropertyAdapterById import jetbrains.mps.smodel.adapter.structure.ref.SReferenceLinkAdapterById import org.jetbrains.mps.openapi.language.SContainmentLink import org.jetbrains.mps.openapi.model.SNode +import org.modelix.incremental.DependencyTracking import org.modelix.model.api.BuiltinLanguages import org.modelix.model.api.ConceptReference import org.modelix.model.api.IChildLink @@ -46,9 +47,15 @@ data class MPSNode(val node: SNode) : IDefaultNodeAdapter { override val reference: INodeReference get() = MPSNodeReference(node.reference) override val concept: IConcept - get() = MPSConcept(node.concept) + get() { + DependencyTracking.accessed(MPSNodeDependency(node)) + return MPSConcept(node.concept) + } override val parent: INode? - get() = node.parent?.let { MPSNode(it) } ?: node.model?.let { MPSModelAsNode(it) } + get() { + DependencyTracking.accessed(MPSContainmentDependency(node)) + return node.parent?.let { MPSNode(it) } ?: node.model?.let { MPSModelAsNode(it) } + } override fun tryGetConcept(): IConcept { return MPSConcept(node.concept) @@ -59,7 +66,10 @@ data class MPSNode(val node: SNode) : IDefaultNodeAdapter { } override val allChildren: Iterable - get() = node.children.map { MPSNode(it) } + get() { + DependencyTracking.accessed(MPSAllChildrenDependency(node)) + return node.children.map { MPSNode(it) } + } override fun removeChild(child: INode) { require(child is MPSNode) { "child must be an MPSNode" } @@ -67,18 +77,26 @@ data class MPSNode(val node: SNode) : IDefaultNodeAdapter { } override fun getPropertyLinks(): List { + DependencyTracking.accessed(MPSNodeDependency(node)) return node.properties.map { MPSProperty(it) } } override fun getReferenceLinks(): List { + DependencyTracking.accessed(MPSNodeDependency(node)) return node.references.map { MPSReferenceLink(it.link) } } override fun getContainmentLink(): IChildLink { + DependencyTracking.accessed(MPSNodeDependency(node)) return node.containmentLink?.let { MPSChildLink(it) } ?: BuiltinLanguages.MPSRepositoryConcepts.Model.rootNodes } override fun getChildren(link: IChildLink): Iterable { + if (link is MPSChildLink) { + DependencyTracking.accessed(MPSChildrenDependency(node, link.link)) + } else { + DependencyTracking.accessed(MPSAllChildrenDependency(node)) + } return node.children.map { MPSNode(it) }.filter { it.getContainmentLink().conformsTo(link) } @@ -132,6 +150,11 @@ data class MPSNode(val node: SNode) : IDefaultNodeAdapter { } override fun getReferenceTarget(link: IReferenceLink): INode? { + if (link is MPSReferenceLink) { + DependencyTracking.accessed(MPSReferenceDependency(node, link.link)) + } else { + DependencyTracking.accessed(MPSAllReferencesDependency(node)) + } return node.references.filter { MPSReferenceLink(it.link).getUID() == link.getUID() } .firstOrNull()?.targetNode?.let { MPSNode(it) } } @@ -153,11 +176,21 @@ data class MPSNode(val node: SNode) : IDefaultNodeAdapter { } override fun getReferenceTargetRef(role: IReferenceLink): INodeReference? { + if (role is MPSReferenceLink) { + DependencyTracking.accessed(MPSReferenceDependency(node, role.link)) + } else { + DependencyTracking.accessed(MPSAllReferencesDependency(node)) + } return node.references.firstOrNull { MPSReferenceLink(it.link).getUID() == role.getUID() } ?.targetNodeReference?.let { MPSNodeReference(it) } } override fun getPropertyValue(property: IProperty): String? { + if (property is MPSProperty) { + DependencyTracking.accessed(MPSPropertyDependency(node, property.property)) + } else { + DependencyTracking.accessed(MPSAllPropertiesDependency(node)) + } val mpsProperty = node.properties.firstOrNull { MPSProperty(it).getUID() == property.getUID() } ?: return null return node.getProperty(mpsProperty) } diff --git a/mps-model-server-plugin/build.gradle.kts b/mps-model-server-plugin/build.gradle.kts index 11b0b73f75..42fe3de97a 100644 --- a/mps-model-server-plugin/build.gradle.kts +++ b/mps-model-server-plugin/build.gradle.kts @@ -12,6 +12,7 @@ val mpsToIdeaMap = mapOf( "2021.3.3" to "213.7172.25", // https://github.com/JetBrains/MPS/blob/2021.3.3/build/version.properties "2022.2" to "222.4554.10", // https://github.com/JetBrains/MPS/blob/2021.2.1/build/version.properties "2022.3" to "223.8836.41", // https://github.com/JetBrains/MPS/blob/2022.3.0/build/version.properties (?) + "2023.2" to "232.10072.27", // https://github.com/JetBrains/MPS/blob/2023.2.0/build/version.properties ) // use the given MPS version, or 2022.2 (last version with JAVA 11) as default val mpsVersion = project.findProperty("mps.version")?.toString().takeIf { !it.isNullOrBlank() } ?: "2020.3.6" @@ -26,10 +27,14 @@ println("Building for MPS version $mpsVersion and IntelliJ version $ideaVersion dependencies { implementation(project(":model-server-lib")) implementation(project(":mps-model-adapters")) - compileOnly("com.jetbrains:mps-openapi:$mpsVersion") - compileOnly("com.jetbrains:mps-core:$mpsVersion") - compileOnly("com.jetbrains:mps-environment:$mpsVersion") - compileOnly("com.jetbrains:mps-platform:$mpsVersion") + + val mpsZip by configurations.creating + mpsZip("com.jetbrains:mps:$mpsVersion") + compileOnly( + zipTree({ mpsZip.singleFile }).matching { + include("lib/*.jar") + }, + ) } // Configure Gradle IntelliJ Plugin diff --git a/ts-model-api/build.gradle.kts b/ts-model-api/build.gradle.kts index a01fad2127..75347d1358 100644 --- a/ts-model-api/build.gradle.kts +++ b/ts-model-api/build.gradle.kts @@ -1,7 +1,6 @@ plugins { base alias(libs.plugins.node) - alias(libs.plugins.ktlint) apply false alias(libs.plugins.npm.publish) }