Skip to content

Commit

Permalink
feat(mps-sync-plugin): support for MPS 2020.3
Browse files Browse the repository at this point in the history
  • Loading branch information
slisson committed Feb 28, 2025
1 parent b6c3aaa commit 6b48f5d
Show file tree
Hide file tree
Showing 14 changed files with 168 additions and 73 deletions.
10 changes: 10 additions & 0 deletions .github/workflows/mps-compatibility.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ jobs:
timeout-minutes: 60

strategy:
fail-fast: false
matrix:
version:
- "2020.3"
Expand Down Expand Up @@ -47,4 +48,13 @@ jobs:
:metamodel-export:build
:mps-model-adapters:build
:mps-model-adapters-plugin:build
:mps-sync-plugin3:build
-Pmps.version.major=${{ matrix.version }}
- name: Archive test report
uses: actions/upload-artifact@v4
if: always()
with:
name: test-report-${{ matrix.version }}
path: |
*/build/test-results
*/build/reports
Original file line number Diff line number Diff line change
Expand Up @@ -90,11 +90,7 @@ data class MPSArea(val repository: SRepository) : IArea, IAreaReference {
}

override fun <T> executeRead(f: () -> T): T {
var result: T? = null
repository.modelAccess.runReadAction {
result = f()
}
return result!!
return repository.computeRead(f)
}

override fun <T> executeWrite(f: () -> T): T {
Expand Down Expand Up @@ -365,3 +361,11 @@ data class MPSArea(val repository: SRepository) : IArea, IAreaReference {
return repository.asLegacyNode()
}
}

fun <R> SRepository.computeRead(body: () -> R): R {
var result: R? = null
modelAccess.runReadAction {
result = body()
}
return result as R
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
package org.modelix.model.mpsadapters

import jetbrains.mps.persistence.MementoImpl
import jetbrains.mps.project.facets.JavaModuleFacet
import jetbrains.mps.smodel.Generator
import jetbrains.mps.util.MacroHelper
import jetbrains.mps.util.MacrosFactory
import org.jetbrains.mps.openapi.module.SRepository
import org.jetbrains.mps.openapi.persistence.Memento
Expand Down Expand Up @@ -30,15 +31,24 @@ data class MPSJavaModuleFacetAsNode(val facet: JavaModuleFacet) : MPSGenericNode
},
BuiltinLanguages.MPSRepositoryConcepts.JavaModuleFacet.path.toReference() to object : IPropertyAccessor<JavaModuleFacet> {
override fun read(facet: JavaModuleFacet): String? {
return facet.classesGen?.let { MacrosFactory().module(facet.module).shrinkPath(it.path) }
// return facet.classesGen?.let { facet.macroHelper().shrinkPath(it.path) }
return null
}

override fun write(element: JavaModuleFacet, value: String?) {
element.classesGen
val memento = MementoImpl()
element.save(memento)
memento.getOrCreateChild("classes").put("path", value?.let { MacrosFactory().module(element.module).expandPath(it) })
element.load(memento)
// val memento = MementoImpl()
// element.save(memento)
// memento.getOrCreateChild("classes").let {
// it.put("generated", "true")
// it.put("path", value.also { println("${element.module} / path = $it") })
// }
// element.load(memento)
}

private fun JavaModuleFacet.macroHelper(): MacroHelper {
return module
.let { if (it is Generator) it.sourceLanguage().sourceModule else it }
.let { MacrosFactory().module(it) }
}
},
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -156,10 +156,10 @@ abstract class MPSModuleAsNode<E : SModule> : MPSGenericNodeAdapter<E>() {
val moduleDescriptor = checkNotNull(module.moduleDescriptor) { "Has no moduleDescriptor: $module" }
val newFacet = FacetsFacade.getInstance().getFacetFactory(JavaModuleFacet.FACET_TYPE)!!.create(element) as JavaModuleFacetImpl
newFacet.load(MementoImpl())
val moduleDir = if (element is Generator) element.getGeneratorLocation() else module.moduleSourceDir
if (moduleDir != null) {
newFacet.setGeneratedClassesLocation(moduleDir.findChild(AbstractModule.CLASSES_GEN))
}
// val moduleDir = if (element is Generator) element.getGeneratorLocation() else module.moduleSourceDir
// if (moduleDir != null) {
// newFacet.setGeneratedClassesLocation(moduleDir.findChild(AbstractModule.CLASSES_GEN))
// }
moduleDescriptor.addFacetDescriptor(newFacet)
module.setModuleDescriptor(moduleDescriptor) // notify listeners
read(element).filterIsInstance<MPSJavaModuleFacetAsNode>().single()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -331,7 +331,7 @@ fun org.jetbrains.mps.openapi.model.SNodeId.tryDecodeModelixReference(): NodeRef
}

fun INodeReference.encodeAsForeignId(): SNodeId {
return SNodeId.Foreign.fromIdNoPrefix("mx" + Hex.encodeHexString(serialize().toByteArray()))
return SNodeId.Foreign("~mx" + Hex.encodeHexString(serialize().toByteArray()))
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
@file:Suppress("removal")

package org.modelix.model.mpsadapters

import jetbrains.mps.extapi.persistence.FileBasedModelRoot
import jetbrains.mps.ide.project.ProjectHelper
import jetbrains.mps.persistence.DefaultModelRoot
import jetbrains.mps.project.AbstractModule
import jetbrains.mps.project.DevKit
import jetbrains.mps.project.MPSExtentions
import jetbrains.mps.project.MPSProject
Expand Down Expand Up @@ -37,7 +40,9 @@ class SolutionProducer(private val myProject: MPSProject) {

private fun createSolutionDescriptor(namespace: String, id: ModuleId, descriptorFile: IFile): SolutionDescriptor {
val descriptor = SolutionDescriptor()
descriptor.outputRoot = "\${module}/source_gen"
// using outputPath instead of outputRoot for backwards compatibility
// descriptor.outputRoot = "\${module}/source_gen"
descriptor.outputPath = descriptorFile.parent!!.findChild("source_gen").path
descriptor.namespace = namespace
descriptor.id = id
val moduleLocation = descriptorFile.parent
Expand Down Expand Up @@ -74,7 +79,9 @@ class LanguageProducer(private val myProject: MPSProject) {

private fun createDescriptor(namespace: String, id: ModuleId, descriptorFile: IFile): LanguageDescriptor {
val descriptor = LanguageDescriptor()
descriptor.outputRoot = "\${module}/source_gen"
// using genPath instead of outputRoot for backwards compatibility
// descriptor.outputRoot = "\${module}/source_gen"
descriptor.genPath = descriptorFile.parent!!.findChild("source_gen").path
descriptor.namespace = namespace
descriptor.id = id
val moduleLocation = descriptorFile.parent
Expand Down Expand Up @@ -127,7 +134,9 @@ class GeneratorProducer(private val myProject: MPSProject) {

private fun createDescriptor(namespace: String, id: ModuleId, alias: String?, generatorModuleLocation: IFile, templateModelsLocation: IFile?): GeneratorDescriptor {
val descriptor = GeneratorDescriptor()
descriptor.outputRoot = "\${module}/${generatorModuleLocation.name}/source_gen"
// using outputPath instead of outputRoot for backwards compatibility
// descriptor.outputRoot = "\${module}/${generatorModuleLocation.name}/source_gen"
descriptor.outputPath = generatorModuleLocation.findChild(AbstractModule.CLASSES_GEN).path
descriptor.namespace = namespace
descriptor.id = id
descriptor.alias = alias ?: "main"
Expand All @@ -142,7 +151,7 @@ class GeneratorProducer(private val myProject: MPSProject) {
}

fun Generator.getGeneratorLocation(): IFile? {
return modelRoots.filterIsInstance<FileBasedModelRoot>().firstNotNullOfOrNull { it.contentDirectory }
return modelRoots.filterIsInstance<FileBasedModelRoot>().mapNotNull { it.contentDirectory }.firstOrNull()
}

class DevkitProducer(private val myProject: MPSProject) {
Expand Down
18 changes: 15 additions & 3 deletions mps-sync-plugin3/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import org.modelix.copyMps
import org.modelix.mpsHomeDir
import org.modelix.mpsMajorVersion
import org.modelix.mpsPlatformVersion

plugins {
`modelix-kotlin-jvm`
Expand Down Expand Up @@ -52,9 +53,6 @@ tasks {
onlyIf {
!setOf(
"2020.3", // incompatible with the intellij plugin
"2021.2", // hangs when executed on CI
"2021.3", // hangs when executed on CI
"2022.2", // hangs when executed on CI
).contains(mpsMajorVersion)
}
jvmArgs("-Dintellij.platform.load.app.info.from.resources=true")
Expand Down Expand Up @@ -91,3 +89,17 @@ publishing {
}
}
}

// disable coroutines agent
if (mpsPlatformVersion < 241) {
afterEvaluate {
val testTask = tasks.test.get()
val originalProviders = testTask.jvmArgumentProviders.toList()
testTask.jvmArgumentProviders.clear()
testTask.jvmArgumentProviders.add(object : CommandLineArgumentProvider {
override fun asArguments(): Iterable<String> {
return originalProviders.flatMap { it.asArguments() }.filterNot { it.contains("coroutines-javaagent.jar") }
}
})
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ 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.model.api.IReadableNode
import org.modelix.model.api.toSerialized
import org.modelix.model.mpsadapters.GlobalModelListener
Expand All @@ -42,7 +42,6 @@ abstract class MPSInvalidatingListener(val repository: SRepository) :
GlobalModelListener(),
SNodeChangeListener,
SModuleListener,
SRepositoryListener,
SModelListener,
org.jetbrains.mps.openapi.model.SModelListener {

Expand Down Expand Up @@ -117,11 +116,11 @@ abstract class MPSInvalidatingListener(val repository: SRepository) :
}

override fun addListener(repository: SRepository) {
repository.addRepositoryListener(this)
repository.addRepositoryListener(srepositoryListener)
}

override fun removeListener(repository: SRepository) {
repository.removeRepositoryListener(this)
repository.removeRepositoryListener(srepositoryListener)
}

override fun propertyChanged(e: SPropertyChangeEvent) {
Expand Down Expand Up @@ -210,9 +209,16 @@ abstract class MPSInvalidatingListener(val repository: SRepository) :
override fun languageAdded(module: SModule, language: SLanguage) { invalidate(module) }
override fun languageRemoved(module: SModule, language: SLanguage) { invalidate(module) }
override fun moduleChanged(module: SModule) { invalidate(module) }
override fun moduleAdded(module: SModule) { invalidate(repository) }
override fun beforeModuleRemoved(module: SModule) {}
override fun moduleRemoved(reference: SModuleReference) { invalidate(repository) }
override fun commandStarted(repository: SRepository) {}
override fun commandFinished(repository: SRepository) {}

/**
* For compatibility with MPS 2020.3, SRepositoryListenerBase is used because SRepositoryListener had the additional
* methods updateStarted and updateFinished in that version.
*/
private val srepositoryListener = object : SRepositoryListenerBase() {
override fun moduleAdded(module: SModule) { invalidate(repository) }
override fun beforeModuleRemoved(module: SModule) {}
override fun moduleRemoved(reference: SModuleReference) { invalidate(repository) }
override fun commandStarted(repository: SRepository) {}
override fun commandFinished(repository: SRepository) {}
}
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
@file:OptIn(ExperimentalTime::class)

package org.modelix.mps.sync3

import com.intellij.openapi.Disposable
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.application.EDT
import com.intellij.openapi.application.ModalityState
import com.intellij.openapi.components.Service
import com.intellij.openapi.components.service
import com.intellij.openapi.project.Project
import jetbrains.mps.ide.project.ProjectHelper
import jetbrains.mps.project.MPSProject
Expand All @@ -17,7 +18,6 @@ import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import kotlinx.coroutines.withContext
import org.jetbrains.mps.openapi.module.SRepository
import org.modelix.model.IVersion
import org.modelix.model.api.TreePointer
Expand All @@ -27,6 +27,7 @@ import org.modelix.model.client2.ModelClientV2
import org.modelix.model.client2.runWrite
import org.modelix.model.lazy.BranchReference
import org.modelix.model.mpsadapters.MPSRepositoryAsNode
import org.modelix.model.mpsadapters.computeRead
import org.modelix.model.sync.bulk.FullSyncFilter
import org.modelix.model.sync.bulk.InvalidatingVisitor
import org.modelix.model.sync.bulk.InvalidationTree
Expand All @@ -38,25 +39,24 @@ import org.modelix.mps.api.ModelixMpsApi
import org.modelix.mps.model.sync.bulk.MPSProjectSyncMask
import org.modelix.mps.sync3.Binding.Companion.LOG
import java.util.concurrent.atomic.AtomicBoolean
import kotlin.time.Duration
import kotlin.time.Duration.Companion.milliseconds
import kotlin.time.Duration.Companion.seconds
import kotlin.math.roundToLong
import kotlin.time.ExperimentalTime

@Service(Service.Level.APP)
class AppLevelModelSyncService() : Disposable {

companion object {
fun getInstance(): AppLevelModelSyncService {
return ApplicationManager.getApplication().service<AppLevelModelSyncService>()
return ApplicationManager.getApplication().getService(AppLevelModelSyncService::class.java)
}
}

private val connections = LinkedHashMap<String, ServerConnection>()
private val coroutinesScope = CoroutineScope(Dispatchers.Default)
private val connectionCheckingJob = coroutinesScope.launchLoop(
BackoffStrategy(
initialDelay = 3.seconds,
maxDelay = 10.seconds,
initialDelay = 3_000,
maxDelay = 10_000,
factor = 1.2,
),
) {
Expand Down Expand Up @@ -97,7 +97,7 @@ class AppLevelModelSyncService() : Disposable {

@Service(Service.Level.PROJECT)
class ModelSyncService(val project: Project) : IModelSyncService, Disposable {
private val mpsProject: MPSProject get() = ProjectHelper.fromIdeaProjectOrFail(project)
private val mpsProject: MPSProject get() = ProjectHelper.fromIdeaProject(project)!!

private val bindings = ArrayList<Binding>()
private val coroutinesScope = CoroutineScope(Dispatchers.IO)
Expand Down Expand Up @@ -210,7 +210,7 @@ class Binding(
while (reason != null) {
i++
if (i % 10 == 0) LOG.debug { "Still waiting for the synchronization to finish: $reason" }
delay(100.milliseconds)
delay(100)
reason = checkInSync()
}
return lastSyncedVersion.getValue()!!
Expand Down Expand Up @@ -347,13 +347,17 @@ class Binding(

private suspend fun <R> writeToMPS(body: () -> R): R {
val result = ArrayList<R>()
withContext(Dispatchers.EDT) {
repository.modelAccess.executeUndoTransparentCommand {
ModelixMpsApi.runWithProject(mpsProject) {
result += body()
ApplicationManager.getApplication().invokeAndWait({
ApplicationManager.getApplication().runWriteAction {
repository.modelAccess.executeUndoTransparentCommand {
ModelixMpsApi.runWithProject(mpsProject) {
result += body()
}
}
}
}
}, ModalityState.NON_MODAL)
// withContext(Dispatchers.Main) {
// }
return result.single()
}

Expand All @@ -379,7 +383,7 @@ class Binding(

val mpsProjects = listOf(mpsProject as MPSProject)
val client = client()
val newVersion = repository.modelAccess.computeReadAction {
val newVersion = repository.computeRead {
fun sync(invalidationTree: IIncrementalUpdateInformation): IVersion? {
return oldVersion.runWrite(client) { branch ->
ModelixMpsApi.runWithProject(mpsProject) {
Expand Down Expand Up @@ -449,14 +453,14 @@ suspend fun jobLoop(
}

class BackoffStrategy(
val initialDelay: Duration = 500.milliseconds,
val maxDelay: Duration = 10.seconds,
val initialDelay: Long = 500,
val maxDelay: Long = 10_000,
val factor: Double = 1.5,
) {
var currentDelay: Duration = initialDelay
var currentDelay: Long = initialDelay

fun failed() {
currentDelay = (currentDelay * factor).coerceAtMost(maxDelay)
currentDelay = (currentDelay * factor).roundToLong().coerceAtMost(maxDelay)
}

fun success() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ package org.modelix.mps.sync3

import com.intellij.openapi.components.service
import com.intellij.openapi.project.Project
import com.intellij.openapi.startup.ProjectActivity
import com.intellij.openapi.startup.StartupActivity

class ModelSyncStartupActivity : ProjectActivity {
override suspend fun execute(project: Project) {
class ModelSyncStartupActivity : StartupActivity {
override fun runActivity(project: Project) {
project.service<ModelSyncService>() // just ensure it's initialized
}
}
Loading

0 comments on commit 6b48f5d

Please sign in to comment.