-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
It was originally implemented in MPS baseLanguage as part of the modelix.mps repository. It is now implemented it in Kotlin and packaged as a version independent IDEA plugin.
- Loading branch information
Showing
18 changed files
with
1,297 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,4 @@ | ||
org.gradle.jvmargs=-Xmx1024m "-XX:MaxMetaspaceSize=2g" | ||
systemProp.org.gradle.internal.http.socketTimeout=120000 | ||
kotlin.daemon.jvmargs=-Xmx2G | ||
kotlin.stdlib.default.dependency = false |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,189 @@ | ||
import org.jetbrains.intellij.tasks.PrepareSandboxTask | ||
import org.jetbrains.kotlin.gradle.dsl.JvmTarget | ||
import org.jetbrains.kotlin.gradle.dsl.KotlinVersion | ||
import java.util.zip.ZipInputStream | ||
|
||
buildscript { | ||
dependencies { | ||
classpath("org.modelix.mps:build-tools-lib:1.3.0") | ||
} | ||
} | ||
|
||
plugins { | ||
id("org.jetbrains.kotlin.jvm") | ||
id("org.jetbrains.intellij") version "1.17.2" | ||
} | ||
|
||
group = "org.modelix.mps" | ||
val mpsVersion = project.findProperty("mps.version").toString() | ||
val mpsPlatformVersion = project.findProperty("mps.platform.version").toString().toInt() | ||
val mpsHome = rootProject.layout.buildDirectory.dir("mps-$mpsVersion") | ||
|
||
java { | ||
sourceCompatibility = JavaVersion.VERSION_11 | ||
targetCompatibility = JavaVersion.VERSION_11 | ||
toolchain { | ||
languageVersion.set(JavaLanguageVersion.of(11)) | ||
} | ||
withSourcesJar() | ||
} | ||
|
||
tasks.compileJava { | ||
options.release = 11 | ||
} | ||
|
||
tasks.compileKotlin { | ||
kotlinOptions { | ||
jvmTarget = "11" | ||
freeCompilerArgs += listOf("-Xjvm-default=all-compatibility") | ||
apiVersion = "1.6" | ||
} | ||
} | ||
|
||
kotlin { | ||
compilerOptions { | ||
jvmTarget.set(JvmTarget.JVM_11) | ||
} | ||
sourceSets { | ||
main { | ||
languageSettings { | ||
apiVersion = KotlinVersion.KOTLIN_1_4.version | ||
} | ||
} | ||
} | ||
} | ||
|
||
dependencies { | ||
fun ModuleDependency.excludedBundledLibraries() { | ||
exclude(group = "org.jetbrains.kotlin") | ||
exclude(group = "org.jetbrains.kotlinx", module = "kotlinx-coroutines-core") | ||
exclude(group = "org.jetbrains.kotlinx", module = "kotlinx-coroutines-jdk8") | ||
} | ||
fun implementationWithoutBundled(dependencyNotation: String) { | ||
implementation(dependencyNotation) { | ||
excludedBundledLibraries() | ||
} | ||
} | ||
|
||
// implementation(coreLibs.ktor.server.html.builder) | ||
implementationWithoutBundled("io.ktor:ktor-server-html-builder:2.3.7") | ||
implementationWithoutBundled("io.ktor:ktor-server-netty:2.3.7") | ||
implementationWithoutBundled("io.ktor:ktor-server-cors:2.3.7") | ||
implementationWithoutBundled("io.ktor:ktor-server-status-pages:2.3.7") | ||
implementationWithoutBundled("io.github.microutils:kotlin-logging:3.0.5") | ||
implementationWithoutBundled("org.jetbrains.kotlinx:kotlinx-coroutines-swing:1.7.3") | ||
|
||
// contains RootDifferencePaneBase | ||
// compileOnly(mpsHome.map { it.files("plugins/mps-vcs/lib/vcs-platform.jar") }) | ||
|
||
testImplementation(coreLibs.kotlin.coroutines.test) | ||
testImplementation(coreLibs.ktor.server.test.host) | ||
testImplementation(coreLibs.ktor.client.cio) | ||
testImplementation(libs.zt.zip) | ||
// testImplementation(libs.modelix.model.server) | ||
// testImplementation(libs.modelix.authorization) | ||
// testImplementation(coreLibs.kotlin.reflect) | ||
// implementation(libs.ktor.server.core) | ||
// implementation(libs.ktor.server.cors) | ||
// implementation(libs.ktor.server.netty) | ||
// implementation(libs.ktor.server.html.builder) | ||
// implementation(libs.ktor.server.auth) | ||
// implementation(libs.ktor.server.auth.jwt) | ||
// implementation(libs.ktor.server.status.pages) | ||
// implementation(libs.ktor.server.forwarded.header) | ||
// testImplementation(coreLibs.ktor.server.websockets) | ||
// testImplementation(coreLibs.ktor.server.content.negotiation) | ||
// testImplementation(coreLibs.ktor.server.resources) | ||
// implementation(libs.ktor.serialization.json) | ||
} | ||
|
||
// Configure Gradle IntelliJ Plugin | ||
// Read more: https://plugins.jetbrains.com/docs/intellij/tools-gradle-intellij-plugin.html | ||
intellij { | ||
localPath = mpsHome.map { it.asFile.absolutePath } | ||
instrumentCode = false | ||
plugins = listOf( | ||
"Git4Idea", | ||
"jetbrains.mps.vcs", | ||
) | ||
} | ||
|
||
tasks { | ||
patchPluginXml { | ||
sinceBuild.set("211") // 203 not supported, because VersionFixer was replaced by ModuleDependencyVersions in 211 | ||
untilBuild.set("232.10072.781") | ||
} | ||
|
||
test { | ||
// tests currently fail for these versions | ||
enabled = !setOf( | ||
211, // jetbrains.mps.vcs plugin cannot be loaded | ||
212, // timeout because of some deadlock | ||
213, // timeout because of some deadlock | ||
222, // timeout because of some deadlock | ||
).contains(mpsPlatformVersion) | ||
} | ||
|
||
buildSearchableOptions { | ||
enabled = false | ||
} | ||
|
||
runIde { | ||
autoReloadPlugins.set(true) | ||
} | ||
|
||
val shortPlatformVersion = mpsVersion.replace(Regex("""20(\d\d)\.(\d+).*"""), "$1$2") | ||
val mpsPluginDir = project.findProperty("mps$shortPlatformVersion.plugins.dir")?.toString()?.let { file(it) } | ||
if (mpsPluginDir != null && mpsPluginDir.isDirectory) { | ||
create<Sync>("installMpsPlugin") { | ||
dependsOn(prepareSandbox) | ||
from(buildDir.resolve("idea-sandbox/plugins/mps-diff-plugin")) | ||
into(mpsPluginDir.resolve("mps-diff-plugin")) | ||
} | ||
} | ||
|
||
withType<PrepareSandboxTask> { | ||
intoChild(pluginName.map { "$it/languages" }) | ||
.from(project.layout.projectDirectory.dir("repositoryconcepts")) | ||
} | ||
|
||
val checkBinaryCompatibility by registering { | ||
group = "verification" | ||
doLast { | ||
val ignoredFiles = setOf( | ||
"META-INF/MANIFEST.MF", | ||
) | ||
fun loadEntries(fileName: String) = rootProject.layout.buildDirectory | ||
.dir("binary-compatibility") | ||
.dir(project.name) | ||
.file(fileName) | ||
.get().asFile.inputStream().use { | ||
val zip = ZipInputStream(it) | ||
val entries = generateSequence { zip.nextEntry } | ||
entries.associate { it.name to "size:${it.size},crc:${it.crc}" } | ||
} - ignoredFiles | ||
val entriesA = loadEntries("a.jar") | ||
val entriesB = loadEntries("b.jar") | ||
val mismatches = (entriesA.keys + entriesB.keys).map { it to (entriesA[it] to entriesB[it]) }.filter { it.second.first != it.second.second } | ||
check(mismatches.isEmpty()) { | ||
"The following files have a different content:\n" + mismatches.joinToString("\n") { " ${it.first}: ${it.second.first} != ${it.second.second}" } | ||
} | ||
} | ||
} | ||
} | ||
|
||
publishing { | ||
publications { | ||
create<MavenPublication>("maven") { | ||
groupId = "org.modelix.mps" | ||
artifactId = "diff-plugin" | ||
artifact(tasks.buildPlugin) { | ||
extension = "zip" | ||
} | ||
} | ||
} | ||
} | ||
|
||
fun Provider<Directory>.dir(name: String): Provider<Directory> = map { it.dir(name) } | ||
fun Provider<Directory>.file(name: String): Provider<RegularFile> = map { it.file(name) } | ||
fun Provider<Directory>.dir(name: Provider<out CharSequence>): Provider<Directory> = flatMap { it.dir(name) } |
Binary file not shown.
145 changes: 145 additions & 0 deletions
145
mps-diff-plugin/src/main/kotlin/org/modelix/ui/diff/DiffHandler.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,145 @@ | ||
package org.modelix.ui.diff | ||
|
||
import com.intellij.openapi.project.ProjectManager | ||
import io.ktor.http.ContentType | ||
import io.ktor.http.HttpStatusCode | ||
import io.ktor.server.application.ApplicationCall | ||
import io.ktor.server.application.call | ||
import io.ktor.server.application.createApplicationPlugin | ||
import io.ktor.server.response.respondOutputStream | ||
import io.ktor.server.response.respondText | ||
import io.ktor.server.routing.Route | ||
import io.ktor.server.routing.get | ||
import io.ktor.server.routing.route | ||
import io.ktor.server.routing.routing | ||
import io.ktor.util.pipeline.PipelineContext | ||
import jetbrains.mps.ide.project.ProjectHelper | ||
import jetbrains.mps.project.MPSProject | ||
import kotlinx.coroutines.CoroutineScope | ||
import kotlinx.coroutines.Deferred | ||
import kotlinx.coroutines.Dispatchers | ||
import kotlinx.coroutines.async | ||
import kotlinx.coroutines.swing.Swing | ||
import kotlinx.coroutines.withContext | ||
import kotlinx.html.HTML | ||
import kotlinx.html.body | ||
import kotlinx.html.br | ||
import kotlinx.html.div | ||
import kotlinx.html.h1 | ||
import kotlinx.html.h2 | ||
import kotlinx.html.html | ||
import kotlinx.html.img | ||
import kotlinx.html.stream.createHTML | ||
import kotlinx.html.style | ||
import java.util.Collections | ||
import javax.imageio.ImageIO | ||
|
||
val DiffHandler = createApplicationPlugin("DiffHandler") { | ||
val handler = DiffHandlerImpl() | ||
application.routing { | ||
handler.apply { installRoutes() } | ||
} | ||
} | ||
|
||
data class DiffRequest(val leftRevision: String, val rightRevision: String) | ||
|
||
class DiffHandlerImpl() { | ||
private val diffRequests = Collections.synchronizedMap(HashMap<DiffRequest, Deferred<List<DiffImage>>>()) | ||
private val coroutineScope = CoroutineScope(Dispatchers.Default) | ||
|
||
fun Route.installRoutes() { | ||
get("/") { | ||
call.respondText("Diff server") | ||
} | ||
get("/clear") { | ||
diffRequests.clear() | ||
call.respondText("Cache cleared") | ||
} | ||
route("/{leftRevision}/{rightRevision}") { | ||
suspend fun PipelineContext<Unit, ApplicationCall>.getImages(): Deferred<List<DiffImage>>? { | ||
val diffRequest = DiffRequest(call.parameters["leftRevision"]!!, call.parameters["rightRevision"]!!) | ||
|
||
var diffResult = diffRequests[diffRequest] | ||
if (diffResult == null) { | ||
val project = (ProjectManager.getInstance().openProjects + ProjectManager.getInstance().defaultProject).firstOrNull() | ||
if (project == null) { | ||
call.respondText(text = "No project found", status = HttpStatusCode.ServiceUnavailable) | ||
return null | ||
} | ||
val mpsProject: MPSProject? = ProjectHelper.fromIdeaProject(project) | ||
if (mpsProject == null) { | ||
call.respondText(text = "MPS project not initialized yet", status = HttpStatusCode.ServiceUnavailable) | ||
return null | ||
} | ||
|
||
diffResult = synchronized(diffRequests) { | ||
if (!diffRequests.containsKey(diffRequest)) { | ||
diffRequests[diffRequest] = coroutineScope.async { | ||
DiffImages(project) | ||
.diffRevisions(diffRequest.leftRevision, diffRequest.rightRevision).flatMap { | ||
// The computation of the diff is not allowed to happen on the EDT, | ||
// but the rendering of the diff dialog has to happen on the EDT. | ||
// | ||
// Using Dispatchers.Swing instead of Dispatchers.Main, because it's not | ||
// initialized in older MPS versions. | ||
withContext(Dispatchers.Swing) { | ||
it() | ||
} | ||
} | ||
} | ||
} | ||
diffRequests[diffRequest] | ||
} | ||
} | ||
return diffResult | ||
} | ||
get("/") { | ||
val images0 = getImages() ?: return@get | ||
val images = images0.await() | ||
call.respondHtmlSafe { | ||
body { | ||
h1 { +"Diff" } | ||
br { } | ||
br { } | ||
for (image in images) { | ||
h2 { | ||
text(image.affectedFile + " - " + image.rootNodePresentation) | ||
} | ||
div { | ||
img(src = image.id + ".png") { | ||
style = "height:auto;max-width:100%;width:${image.size.width}px" | ||
} | ||
br { } | ||
br { } | ||
br { } | ||
} | ||
} | ||
} | ||
} | ||
} | ||
get("{imageId}.png") { | ||
val imageId = call.parameters["imageId"] | ||
val images = getImages() ?: return@get | ||
val image = images.await().find { it.id == imageId } | ||
if (image == null) { | ||
call.respondText("Image with ID $imageId not found", status = HttpStatusCode.NotFound) | ||
} else { | ||
call.respondOutputStream(contentType = ContentType.Image.PNG) { | ||
ImageIO.write(image.image, "png", this) | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
|
||
/** | ||
* respondHtml fails to respond anything if an exception is thrown in the body and an error handler is installed | ||
* that tries to respond an error page. | ||
*/ | ||
suspend fun ApplicationCall.respondHtmlSafe(status: HttpStatusCode = HttpStatusCode.OK, block: HTML.() -> Unit) { | ||
val htmlText = createHTML().html { | ||
block() | ||
} | ||
respondText(htmlText, ContentType.Text.Html, status) | ||
} |
Oops, something went wrong.