Q35OTML226J2HZLHOCPV5OY6ZUM2XU4RZBE5E3GDWVHVFASHFEJAC B43WNBLFFR2UQIH3C6KIZAQTAEQOQM3J3IYLGQMVGJHYOME73OKQC FNNW5IEAXQ43WKB6QSQB7DFLG3Y3T5FYPXIUX7KQ2URR2GU3QLTAC 5AUENX2YJVFNKZUSPEPDNLLL7TKZS2WTFC6CABWSZK2EC4MNCRQAC QXUEMZ3B2FUHFUC7ZZHJMH5FVWLEMEYXUMFA6JNXTJKIVZNMRIOAC 7L5LODGZ7AN4ZULDJZMLALD7PL6E57VZSNNSG67SFJARUJGCT47QC ZCRW57C5MSBXYGUMGQTZNHGHO4HGHFBICW53X5I2IMGP3H2CKWRQC FRFFQV7VNYKGCA7ZAOSRPC2HHYTAIZ6AGGR7A5QEV6QPAQGFDYGAC 6CR2EFUN7JXFHCBTNX3WWOOP4WFOCFO6KSPEBN6V6J5HFZO2LHNQC OPFG6CZ26PPTGTH7ULLRQGZGR3YEIEJOV5W2E3WN7PFRZS62CVLQC import com.github.jonathanxd.dracon.cache.HashKeyimport com.github.jonathanxd.dracon.cache.HashMapCacheimport com.github.jonathanxd.dracon.cache.PijulCacheimport com.github.jonathanxd.dracon.cache.PijulLogEntryCacheimport com.github.jonathanxd.dracon.cache.persist.DraconPersistentStateComponent
val change = this.doExecutionWithMapper("change-$hash", this.createPainlessExecPijulOperation(project, root, listOf("change", hash))) {it.parseChange(hash)
val change = this.pijulLogEntryCache.queryOrCompute(hash) {this.doExecutionWithMapper("change-$hash",this.createPainlessExecPijulOperation(project, root, listOf("change", hash))) {it.parseChange(hash)}
val change = this.doExecutionWithMapper("change-$hash", this.createPainlessExecPijulOperation(project, root, listOf("change", hash))) {it.parseChange(hash)
val change = this.pijulLogEntryCache.queryOrCompute(hash) {this.doExecutionWithMapper("change-$hash",this.createPainlessExecPijulOperation(project, root, listOf("change", hash))) {it.parseChange(hash)}
return PijulExecution(input.linesToFlow().onEach {draconConsoleWriter(project).logCommand("pijul", args, it)},error.linesToFlow().onEach {draconConsoleWriter(project).logCommandError("pijul", args, it)},flow {while (process.isAlive)delay(delay)val exit = process.exitValue()
input.split("\n").forEach {draconConsoleWriter(project).logCommand("pijul", args, it)}error.split("\n").forEach {draconConsoleWriter(project).logCommandError("pijul", args, it)}
if (exit == 0) {draconConsoleWriter(project).logCommand("pijul", args, "<Exit status> $exit")} else {draconConsoleWriter(project).logCommandError("pijul", args, "<Exit status> $exit")}
val exit = process.waitFor()if (exit == 0) {draconConsoleWriter(project).logCommand("pijul", args, "<Exit status> $exit")} else {draconConsoleWriter(project).logCommandError("pijul", args, "<Exit status> $exit")}
val input = process.inputStreamval error = process.errorStream
val input = String(process.inputStream.readAllBytes(), Charsets.UTF_8)val error = String(process.errorStream.readAllBytes(), Charsets.UTF_8)input.split("\n").forEach {draconConsoleWriter(project).logCommand("pijul", args, it)}error.split("\n").forEach {draconConsoleWriter(project).logCommandError("pijul", args, it)}val exit = process.waitFor()if (exit == 0) {draconConsoleWriter(project).logCommand("pijul", args, "<Exit status> $exit")} else {draconConsoleWriter(project).logCommandError("pijul", args, "<Exit status> $exit")}
input.linesToFlow().onEach {
input,error,exit)}/*** Creates a [PijulExecution] operation that could be executed at any time. This operation uses Kotlin Coroutines* and can be executed immediately through [doExecution] or through [doExecutionWithMapper].** This implementation does not requires a delay value to be provided, like [createExecPijulOperation] does, instead* it uses the kotlin conversion from `CompletionStage` to `Coroutines` and awaits the process through [Process.onExit].** [doExecution] and [doExecutionWithMapper] does execution by scheduling task to [Dispatchers.IO], instead of Main Thread,* offloading the Process execution handling to a different scheduler. However, mapping operation of [doExecutionWithMapper]* is not offloaded from the caller context.**/@RequiresBackgroundThreadprivate fun <K: Any> createPainlessExecPijulOperationWithCache(project: Project,dir: Path,args: List<String>,key: K): PijulExecution {return this.cache.cache(key) {val process = ProcessBuilder().command(listOf(this.findPijul()) + args).directory(dir.toFile()).start()val input = String(process.inputStream.readAllBytes(), Charsets.UTF_8)val error = String(process.errorStream.readAllBytes(), Charsets.UTF_8)input.split("\n").forEach {
val exit = process.exitValue()if (exit == 0) {draconConsoleWriter(project).logCommand("pijul", args, "<Exit status> $exit")} else {draconConsoleWriter(project).logCommandError("pijul", args, "<Exit status> $exit")}
val exit = process.waitFor()if (exit == 0) {draconConsoleWriter(project).logCommand("pijul", args, "<Exit status> $exit")} else {draconConsoleWriter(project).logCommandError("pijul", args, "<Exit status> $exit")}
return PijulExecution(input.linesToFlow().onEach {draconConsoleWriter(project).logCommand("pijul", args, it)},error.linesToFlow().onEach {draconConsoleWriter(project).logCommandError("pijul", args, it)},flow {process.onExit().await()
input.split("\n").forEach {draconConsoleWriter(project).logCommand("pijul", args, it)}
if (exit == 0) {draconConsoleWriter(project).logCommand("pijul", args, "<Exit status> $exit")} else {draconConsoleWriter(project).logCommandError("pijul", args, "<Exit status> $exit")}
val exit = process.waitFor()if (exit == 0) {draconConsoleWriter(project).logCommand("pijul", args, "<Exit status> $exit")} else {draconConsoleWriter(project).logCommandError("pijul", args, "<Exit status> $exit")}
return PijulExecution(input.linesToFlow().onEach {draconConsoleWriter(project).logCommand("pijul", args, it)},error.linesToFlow().onEach {draconConsoleWriter(project).logCommandError("pijul", args, it)},flow {process.onExit().await()
input.split("\n").forEach {draconConsoleWriter(project).logCommand("pijul", args, it)}
if (exit == 0) {draconConsoleWriter(project).logCommand("pijul", args, "<Exit status> $exit")} else {draconConsoleWriter(project).logCommandError("pijul", args, "<Exit status> $exit")}
val exit = process.waitFor()if (exit == 0) {draconConsoleWriter(project).logCommand("pijul", args, "<Exit status> $exit")} else {draconConsoleWriter(project).logCommandError("pijul", args, "<Exit status> $exit")}
package com.github.jonathanxd.dracon.cache.persistimport com.github.jonathanxd.dracon.log.PijulLogimport com.github.jonathanxd.dracon.log.PijulLogEntryimport com.google.gson.Gsonimport com.google.gson.GsonBuilderimport com.google.gson.reflect.TypeTokenimport com.intellij.util.xmlb.annotations.Propertyimport java.io.ByteArrayInputStreamimport java.io.ByteArrayOutputStreamimport java.io.ObjectInputStreamimport java.io.ObjectOutputStreamimport java.util.*
package com.github.jonathanxd.dracon.cache.persistimport com.github.jonathanxd.dracon.log.PijulLogEntryimport com.intellij.openapi.components.*import com.intellij.util.xmlb.XmlSerializerUtilimport java.io.*import java.nio.file.Filesimport java.nio.file.Pathimport java.nio.file.StandardOpenOptionimport java.util.*import java.util.zip.GZIPInputStreamimport java.util.zip.GZIPOutputStream@State(name = "DraconLogEntries", storages = [Storage(value = StoragePathMacros.CACHE_FILE)], reloadable = true)@Serviceclass DraconPersistentStateComponent() : PersistentStateComponent<DraconPersistentStateComponent> {public var data: String? = nulloverride fun getState() = thisoverride fun loadState(state: DraconPersistentStateComponent) {XmlSerializerUtil.copyBean(state, this);}}fun String.toPijulLogEntries(): List<PijulLogEntry> {val decoded = Base64.getDecoder().decode(this)val reader = ObjectInputStream(ByteArrayInputStream(decoded))return reader.readObject() as List<PijulLogEntry>}fun createLogEntriesPersist(entries: List<PijulLogEntry>): String {val stream = ByteArrayOutputStream()val writer = ObjectOutputStream(stream)writer.writeObject(entries)writer.close()return Base64.getEncoder().encodeToString(stream.toByteArray())}fun Path.toPijulLogEntriesMap(): Map<String, PijulLogEntry> {Files.newInputStream(this).use { stream ->GZIPInputStream(stream).use { gz ->ObjectInputStream(gz).use { reader ->return reader.readObject() as Map<String, PijulLogEntry>}}}}fun String.toPijulLogEntriesMap(): Map<String, PijulLogEntry> {val decoded = Base64.getDecoder().decode(this)val reader = ObjectInputStream(GZIPInputStream(ByteArrayInputStream(decoded)))return reader.readObject() as Map<String, PijulLogEntry>}fun Path.createLogEntriesPersist(entries: Map<String, PijulLogEntry>) {Files.newOutputStream(this, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING).use { writer ->GZIPOutputStream(writer).use { compress ->ObjectOutputStream(compress).use {oos ->oos.writeObject(entries)}}}}
package com.github.jonathanxd.dracon.cacheimport com.github.jonathanxd.dracon.cache.persist.createLogEntriesPersistimport com.github.jonathanxd.dracon.cache.persist.toPijulLogEntriesMapimport com.github.jonathanxd.dracon.log.PijulLogEntryimport com.github.jonathanxd.dracon.pijul.PijulOperationResultimport com.github.jonathanxd.dracon.pijul.SuccessStatusCodeimport com.intellij.openapi.project.Projectimport com.intellij.openapi.project.getProjectDataPathimport java.nio.file.Filesimport java.util.concurrent.ConcurrentHashMap@Suppress("UnstableApiUsage")class PijulLogEntryCache(val project: Project) {private val map = ConcurrentHashMap<String, PijulLogEntry>()private val cachePath = project.getProjectDataPath("com.jonathanxd.dracon")private val cacheFile = cachePath.resolve("log.obj")init {Files.createDirectories(cachePath)if (Files.exists(this.cacheFile)) {try {this.map.putAll(this.cacheFile.toPijulLogEntriesMap())} catch (e: Throwable) {Files.delete(this.cacheFile)}}}fun queryOrCompute(hash: String, compute: () -> PijulOperationResult<PijulLogEntry>): PijulOperationResult<PijulLogEntry> {return if (!this.map.containsKey(hash)) {val computeEntry = compute()if (computeEntry.statusCode !is SuccessStatusCode) {return computeEntry}this.map[hash] = computeEntry.result!!this.cacheFile.createLogEntriesPersist(this.map)computeEntry} else {PijulOperationResult("change-$hash", SuccessStatusCode, this.map[hash])}}}
@file:Suppress("UNCHECKED_CAST")package com.github.jonathanxd.dracon.cacheimport com.github.jonathanxd.dracon.pijul.PijulExecutionimport java.util.concurrent.ConcurrentHashMapimport java.util.concurrent.Executors/*** Some changes are so big that it takes a long time to retrieve, refreshing the view would cause* the entire change tree to be retrieved again from Pijul, taking a long time again, this cache tries to alleviate* this by caching the result of `Pijul` command.** It in fact, does not cache the result of `pijul` command, it could cache anything, however an update function* must be provided when querying the cache. The update function must be a pure function and the function identity* must be tied to the `key`, in other words, the `key` is the identity of the function. This constraint must never be broken,* because [PijulCache] does no update stored functions, the function is stored only when there is no cache data for the `key`,* subsequent calls will ignore the provided function.*/interface PijulCache {/*** Retrieves cached value, or computes if absent.*/fun <K: Any, V: Any> cache(key: K, updateFn: () -> V): V/*** Request the cache to be updated, it does not mean that the cache will be updated in the time this is invoked.*/fun <K: Any> requestUpdate(key: K)}class HashMapCache : PijulCache {private val cache = ConcurrentHashMap<Any, Cache<*>>()private val updateExecutor = Executors.newCachedThreadPool()override fun <K: Any, V: Any> cache(key: K, updateFn: () -> V): V {return (this.cache.computeIfAbsent(key) {val update = updateFn()Cache(update, updateFn)} as Cache<V>).value}override fun <K : Any> requestUpdate(key: K) {val fn = this.cache[key]?.updateFnif (fn != null) {updateExecutor.submit {val updated = fn()this.cache[key] = Cache(updated, fn)}}}}data class HashKey(val hash: String)data class Cache<V: Any>(var value: V, var updateFn: (() -> V)?)data class CommandResult(val stdOut: String, val stdErr: String)