ISO7J5ZH5UB7NFZKTKKJQHQHCP4DWQ3F7SM2NDMVYJAGGIKDLX4QC Q35OTML226J2HZLHOCPV5OY6ZUM2XU4RZBE5E3GDWVHVFASHFEJAC 7L5LODGZ7AN4ZULDJZMLALD7PL6E57VZSNNSG67SFJARUJGCT47QC GGYFPXND4VBCROZZXTKAP7Y4JOP2OOYQAFVLMUE7SLFM225EUSIAC MTPTFTHGAOKQGRUDXC55AM6XJHZZZ5EF6FPVXKFVCUYVXJNEANYQC ZCRW57C5MSBXYGUMGQTZNHGHO4HGHFBICW53X5I2IMGP3H2CKWRQC FNNW5IEAXQ43WKB6QSQB7DFLG3Y3T5FYPXIUX7KQ2URR2GU3QLTAC EAGIDXOLFTHZMZ77ZWAM7MVVUBBXJMZD7RZUNBHJPYRFGGKILGVAC OPFG6CZ26PPTGTH7ULLRQGZGR3YEIEJOV5W2E3WN7PFRZS62CVLQC 6CR2EFUN7JXFHCBTNX3WWOOP4WFOCFO6KSPEBN6V6J5HFZO2LHNQC FRFFQV7VNYKGCA7ZAOSRPC2HHYTAIZ6AGGR7A5QEV6QPAQGFDYGAC Q7FXTHVUPVAFMNY277C3NFJO3VXLZU5G6C6UYSD5QPURHSG3A7OQC QXUEMZ3B2FUHFUC7ZZHJMH5FVWLEMEYXUMFA6JNXTJKIVZNMRIOAC 5AUENX2YJVFNKZUSPEPDNLLL7TKZS2WTFC6CABWSZK2EC4MNCRQAC B43WNBLFFR2UQIH3C6KIZAQTAEQOQM3J3IYLGQMVGJHYOME73OKQC val change = changesMap.computeIfAbsent(rootPath) {pijul(this.project).diff(this.project, root).result} ?: continue
val head = this.logEntryChangeCache.loadRevision(rootAbsolute) {pijul(this.project).latestRevisionNumber(this.project, rootPath)}.result ?: continue
package com.github.jonathanxd.dracon.pijulimport com.intellij.openapi.vcs.FileStatusimport java.nio.file.Pathdata class PijulFilesStatus(val changed: Map<Path, List<FileStatus>>,val tracked: List<Path>)
data class Author(val name: String?, val fullName: String?, val email: String?): Serializable
data class Author(val name: String?, val fullName: String?, val email: String?): Serializable {companion object {private const val serialVersionUID: Long = 1L}}
package com.github.jonathanxd.dracon.listenersimport com.github.jonathanxd.dracon.cache.FileStatusCacheimport com.github.jonathanxd.dracon.cache.PijulLogEntryChangeCacheimport com.intellij.openapi.components.serviceimport com.intellij.openapi.externalSystem.autoimport.AsyncFileChangeListenerBaseimport com.intellij.openapi.project.Projectimport com.intellij.openapi.roots.ProjectRootManagerimport com.intellij.openapi.vfs.AsyncFileListenerimport com.intellij.openapi.vfs.VirtualFileimport com.intellij.openapi.vfs.newvfs.events.VFileEventimport com.intellij.vcsUtil.VcsUtilimport java.nio.file.Pathsimport java.util.concurrent.CopyOnWriteArrayListclass PijulAsyncFileListener(val project: Project) : AsyncFileChangeListenerBase(), AsyncFileListener {private val fileStatusCache by lazy { this.project.service<FileStatusCache>() }private val pijulLogEntryChangeCache by lazy { this.project.service<PijulLogEntryChangeCache>() }override fun apply() {}override fun init() {}override fun isRelevant(file: VirtualFile, event: VFileEvent): Boolean {return ProjectRootManager.getInstance(this.project).fileIndex.isInContent(file)}override fun updateFile(file: VirtualFile, event: VFileEvent) {if (ProjectRootManager.getInstance(this.project).fileIndex.isInContent(file)) {this.fileStatusCache.unload(Paths.get(VcsUtil.getFilePath(file).path))this.pijulLogEntryChangeCache.unload(Paths.get(VcsUtil.getFilePath(file).path))}}}
class PijulVcsContext(val project: Project) {val root by lazy {
class PijulVcsContext(val project: Project): Disposable {init {VirtualFileManager.getInstance().addAsyncFileListener(PijulAsyncFileListener(project),this)}override fun dispose() {}val root: Path get() {
ProjectLevelVcsManager.getInstance(this.project).getRootsUnderVcs(pijulVcs(this.project)).first().toNioPath()
return ProjectLevelVcsManager.getInstance(this.project).getRootsUnderVcs(pijulVcs(this.project)).firstOrNull()?.toNioPath() ?: Paths.get(project.basePath!!)
import kotlinx.coroutines.flow.*import kotlinx.coroutines.future.asDeferredimport kotlinx.coroutines.future.awaitimport kotlinx.coroutines.runBlockingimport java.io.Fileimport java.net.ServerSocket
val fileStatusBasedInPijulLs = this.doExecutionWithMapper("file_status_from_ls", this.createExecPijulOperation(project, rootPath, listOf("ls"), delay = 10L)) {
val fileStatusBasedInPijulLs =this.doExecutionWithMapper("file_status_from_ls", this.createExecPijulOperation(project, rootPath, listOf("ls"), log = false)) {
}}@OptIn(ExperimentalPathApi::class)@RequiresBackgroundThreadoverride fun fileStatus(project: Project, file: Path): PijulOperationResult<FileStatus> {val root = project.service<PijulVcsContext>().rootval execution = this.createPainlessExecPijulOperation(project, root, listOf("diff", "--json"))val fileStatusBasedInPijulDiff = this.doExecutionWithMapper("file_status", execution) {try {val changes = PijulDiffJson.parseJson(it)val changeMap = changes.toFileStatusMap()val fPath = file.relativeTo(root).toString()if (fPath.isEmpty()) {FileStatus.SUPPRESSED} else {changeMap[fPath]?.firstOrNull()}} catch(t: Throwable) {logger<PijulCmd>().error(t)null}}val fileStatusBasedInPijulLs =this.doExecutionWithMapper("file_status_from_ls", this.createExecPijulOperation(project, root, listOf("ls"), log = false)) {val trackedFiles = it.split("\n")val fPath = file.relativeTo(root).toString()if (trackedFiles.contains(fPath)) {FileStatus.NOT_CHANGED} else {FileStatus.UNKNOWN}}return if (fileStatusBasedInPijulDiff.statusCode !is SuccessStatusCode|| fileStatusBasedInPijulDiff.result == null|| fileStatusBasedInPijulDiff.result == FileStatus.SUPPRESSED) {fileStatusBasedInPijulLs} else {fileStatusBasedInPijulDiff}}override fun fileStatusMap(project: Project): PijulOperationResult<PijulFilesStatus> {val root = project.service<PijulVcsContext>().rootval fileStatusMap = mutableMapOf<Path, List<FileStatus>>()val tracked = mutableListOf<Path>()val execution = this.createPainlessExecPijulOperation(project, root, listOf("diff", "--json"))val fileStatusBasedInPijulDiff = this.doExecutionWithMapper("file_status", execution) {try {val changes = PijulDiffJson.parseJson(it)val changeMap = changes.toFileStatusMap()for (c in changeMap) {fileStatusMap[root.resolve(c.key)] = c.value}} catch(t: Throwable) {logger<PijulCmd>().error(t)null}}val fileStatusBasedInPijulLs =this.doExecutionWithMapper("file_status_from_ls", this.createExecPijulOperation(project, root, listOf("ls"), log = false)) {it.split("\n").mapTo(tracked) {root.resolve(it)}}if (fileStatusBasedInPijulDiff.statusCode !is SuccessStatusCode) {return fileStatusBasedInPijulDiff as PijulOperationResult<PijulFilesStatus>
val rollback = this.doExecution("rollback_$rollbackHash",this.createPainlessExecPijulOperation(project, root, listOf("unrecord", "--reset", rollbackHash)))if (rollback.statusCode !is SuccessStatusCode) {return rollback as PijulOperationResult<Boolean>}
val rollback = this.doExecution("rollback_from_${hashList[0]}_until_$hash",this.createPainlessExecPijulOperation(project, root, listOf("unrecord", "--reset") + rollbacks))
entry.hunks.filterIsInstance<HunkWithPath>().map { hunk ->
entry.hunks.filterIsInstance<HunkWithPath>().groupBy { it.resolvePath(ctx.root) }.map { (p, hunks) ->val hunk = hunks.firstOrNull { it is EditHunk }?: hunks.firstOrNull { it is ReplacementHunk }?: hunks.firstOrNull { it is FileDelHunk }?: hunks.firstOrNull { it is FileAddHunk }?: hunks.first()
package com.github.jonathanxd.dracon.cacheimport com.github.jonathanxd.dracon.context.PijulVcsContextimport com.github.jonathanxd.dracon.log.PijulLogEntryimport com.github.jonathanxd.dracon.pijul.PijulOperationResultimport com.github.jonathanxd.dracon.pijul.SuccessStatusCodeimport com.github.jonathanxd.dracon.revision.PijulRevisionNumberimport com.intellij.openapi.components.Serviceimport com.intellij.openapi.components.serviceimport com.intellij.openapi.project.Projectimport java.nio.file.Pathimport kotlin.io.path.ExperimentalPathApiimport kotlin.io.path.relativeTo@Suppress("UnstableApiUsage")@OptIn(ExperimentalPathApi::class)@Serviceclass PijulLogEntryChangeCache(val project: Project) {private val dataCache = DataCache<String, PijulLogEntry>(project, "path_change")private val revisionDataCache = DataCache<String, PijulRevisionNumber>(project, "revision")fun load(path: Path, compute: () -> PijulOperationResult<PijulLogEntry>): PijulOperationResult<PijulLogEntry> {val ctx = this.project.service<PijulVcsContext>()val relativePath = path.relativeTo(ctx.root).toString()return this.dataCache.queryOrLoad(relativePath,{ compute() },{ it.statusCode is SuccessStatusCode && it.result != null },{it.result!!},{ PijulOperationResult("file_changes", SuccessStatusCode, it) })}fun loadRevision(path: Path, compute: () -> PijulOperationResult<PijulRevisionNumber>): PijulOperationResult<PijulRevisionNumber> {val ctx = this.project.service<PijulVcsContext>()val relativePath = path.relativeTo(ctx.root).toString()return this.revisionDataCache.queryOrLoad(relativePath,{ compute() },{ it.statusCode is SuccessStatusCode },{it.result!!},{ PijulOperationResult("revision", SuccessStatusCode, it) })}fun unload(path: Path) {val ctx = this.project.service<PijulVcsContext>()val relativePath = path.relativeTo(ctx.root).toString()this.dataCache.unload(relativePath)this.dataCache.unload("")this.revisionDataCache.unload(relativePath)this.revisionDataCache.unload("")}}
private val map = ConcurrentHashMap<String, PijulLogEntry>()private val cachePath = project.getProjectDataPath("com.jonathanxd.dracon")private val cacheFile = cachePath.resolve("log.obj")
private val dataCache = DataCache<String, PijulLogEntry>(project, "change")
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])}
fun load(hash: String, compute: () -> PijulOperationResult<PijulLogEntry>): PijulOperationResult<PijulLogEntry> {return this.dataCache.queryOrLoad(hash,{ compute() },{ it.statusCode is SuccessStatusCode },{it.result!!},{ PijulOperationResult("change-$hash", SuccessStatusCode, it) })
package com.github.jonathanxd.dracon.cacheimport com.github.jonathanxd.dracon.context.PijulVcsContextimport com.github.jonathanxd.dracon.pijul.PijulOperationResultimport com.github.jonathanxd.dracon.pijul.SuccessStatusCodeimport com.github.jonathanxd.dracon.pijul.pijulimport com.intellij.openapi.components.Serviceimport com.intellij.openapi.components.serviceimport com.intellij.openapi.project.Projectimport com.intellij.openapi.vcs.FileStatusimport com.intellij.openapi.vcs.FileStatusFactoryimport java.io.Serializableimport java.nio.file.Pathimport kotlin.io.path.ExperimentalPathApiimport kotlin.io.path.relativeTo@OptIn(ExperimentalPathApi::class)@Serviceclass FileStatusCache(val project: Project) {private val cache = DataCache<String, CacheablePijulFileStatus>(this.project, "file_status")init {// Pre-load statusthis.cache.lock()try {val ctx = this.project.service<PijulVcsContext>()val map = pijul(project).fileStatusMap(project)if (map.result != null) {for (change in map.result.changed) {if (change.value.isNotEmpty()) {val path = change.key.relativeTo(ctx.root)this.cache.queryOrLoad(path.toString()) {change.value.first().toPijul()}}}for (tracked in map.result.tracked) {val path = tracked.relativeTo(ctx.root)this.cache.queryOrLoad(path.toString()) {FileStatus.NOT_CHANGED.toPijul()}}}} finally {this.cache.unlock()}}fun load(path: Path): PijulOperationResult<FileStatus> {val ctx = this.project.service<PijulVcsContext>()return this.cache.queryOrLoad(path.relativeTo(ctx.root).toString(),{ pijul(project).fileStatus(project, path) },{ it.statusCode is SuccessStatusCode },{ it.result!!.toPijul() },{ PijulOperationResult("file_status", SuccessStatusCode, it.toFileStatus()) })}fun unload(path: Path) {val ctx = this.project.service<PijulVcsContext>()val relativePath = path.relativeTo(ctx.root).toString()this.cache.unload(relativePath)}}data class CacheablePijulFileStatus(val id: String) : Serializable {companion object {private const val serialVersionUID: Long = 1}}fun FileStatus.toPijul(): CacheablePijulFileStatus = CacheablePijulFileStatus(this.id)fun CacheablePijulFileStatus.toFileStatus(): FileStatus =try {FileStatus::class.java.getDeclaredField(this.id).get(null) as FileStatus} catch (t: Throwable) {FileStatusFactory.getInstance().allFileStatuses.first {it.id == this.id}}
package com.github.jonathanxd.dracon.cacheimport com.github.jonathanxd.dracon.context.PijulVcsContextimport com.github.jonathanxd.dracon.revision.PijulVcsFileRevisionimport com.github.jonathanxd.dracon.revision.loadStateInRevisionimport com.intellij.openapi.components.Serviceimport com.intellij.openapi.components.serviceimport com.intellij.openapi.project.Projectimport java.io.Serializableimport java.util.concurrent.CompletableFutureimport java.util.concurrent.Executorsimport java.util.concurrent.locks.ReentrantLock@Serviceclass FileRevisionCache(val project: Project) {private val cache = DataCache<FileRevisionRef, ByteArray>(this.project, "file_revision")fun loadAsync(rev: PijulVcsFileRevision): CompletableFuture<ByteArray> {val root = project.service<PijulVcsContext>().rootval fileRev = FileRevisionRef(rev.filePath.toAbsolutePath().toString(), rev.revision.hash)return this.cache.queryOrLoadAsync(fileRev) {loadStateInRevision(rev.revision.hash,this.project,root,rev.filePath).toByteArray(Charsets.UTF_8)}}}data class FileRevisionRef(val filePath: String,val revision: String): Serializable
package com.github.jonathanxd.dracon.cacheimport com.intellij.openapi.project.Projectimport com.intellij.openapi.project.getProjectDataPathimport java.io.ObjectInputStreamimport java.io.ObjectOutputStreamimport java.nio.file.Filesimport java.nio.file.Pathimport java.nio.file.StandardOpenOptionimport java.util.concurrent.CompletableFutureimport java.util.concurrent.ConcurrentHashMapimport java.util.concurrent.Executorsimport java.util.concurrent.locks.ReentrantLockimport java.util.zip.GZIPInputStreamimport java.util.zip.GZIPOutputStream@Suppress("UnstableApiUsage")class DataCache<K, V>(val project: Project,val name: String) {private val cachePath = project.getProjectDataPath("com.github.jonathanxd.dracon")private val cacheFile = cachePath.resolve("$name.gz")private val manager = DataCacheManager<K, V>(this.cacheFile)private val lock = ReentrantLock()private val updateExecutor = Executors.newCachedThreadPool()private val inMemory = ConcurrentHashMap<K, V>()init {this.manager.load()?.let {this.inMemory.putAll(it)}}fun lock() {this.lock.lock()}fun unlock() {this.lock.unlock()}fun unload(key: K) {this.lock.lock()try {this.inMemory.remove(key)this.manager.write(this.inMemory)} finally {this.lock.unlock()}}fun queryOrLoad(key: K, compute: (K) -> V): V {this.lock.lock()try {return if (!this.inMemory.containsKey(key)) {val computeEntry = compute(key)this.inMemory[key] = computeEntrythis.manager.write(this.inMemory)computeEntry} else {this.inMemory[key]!!}} finally {this.lock.unlock();}}fun <I> queryOrLoad(key: K,compute: (K) -> I,filter: (I) -> Boolean,iMapper: (I) -> V,vMapper: (V) -> I): I {this.lock.lock()try {return if (!this.inMemory.containsKey(key)) {val computeEntry = compute(key)if (!filter(computeEntry)) {return computeEntry}this.inMemory[key] = iMapper(computeEntry!!)this.manager.write(this.inMemory)computeEntry} else {vMapper(this.inMemory[key]!!)}} finally {this.lock.unlock();}}fun queryOrLoadAsync(key: K, compute: (K) -> V): CompletableFuture<V> {if (this.inMemory.containsKey(key)) {return CompletableFuture.completedFuture(this.inMemory[key]!!)} else {return CompletableFuture.supplyAsync({this.lock.lock()try {val computeEntry = compute(key)this.inMemory[key] = computeEntrythis.manager.write(this.inMemory)computeEntry} finally {this.lock.unlock()}}, this.updateExecutor)}}fun <I> queryOrLoadAsync(key: K,compute: (K) -> I,filter: (I) -> Boolean,iMapper: (I) -> V,vMapper: (V) -> I): CompletableFuture<I> {if (this.inMemory.containsKey(key)) {return CompletableFuture.completedFuture(vMapper(this.inMemory[key]!!))} else {return CompletableFuture.supplyAsync({this.lock.lock()try {val computeEntry = compute(key)if (!filter(computeEntry)) {computeEntry} else {this.inMemory[key] = iMapper(computeEntry!!)this.manager.write(this.inMemory)computeEntry}} finally {this.lock.unlock()}}, this.updateExecutor)}}}class DataCacheManager<K, V>(val path: Path) {init {Files.createDirectories(this.path.parent)}fun load(): Map<K, V>? {if (!Files.exists(this.path)) return nulltry {Files.newInputStream(this.path).use { stream ->GZIPInputStream(stream).use { gz ->ObjectInputStream(gz).use { reader ->return reader.readObject() as Map<K, V>}}}} catch (t: Throwable) {t.printStackTrace()Files.delete(this.path)return null}}fun write(data: Map<K, V>) {Files.newOutputStream(this.path, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING).use { writer ->GZIPOutputStream(writer).use { compress ->ObjectOutputStream(compress).use { oos ->oos.writeObject(data)}}}}}