B43WNBLFFR2UQIH3C6KIZAQTAEQOQM3J3IYLGQMVGJHYOME73OKQC
5AUENX2YJVFNKZUSPEPDNLLL7TKZS2WTFC6CABWSZK2EC4MNCRQAC
ZCRW57C5MSBXYGUMGQTZNHGHO4HGHFBICW53X5I2IMGP3H2CKWRQC
MTPTFTHGAOKQGRUDXC55AM6XJHZZZ5EF6FPVXKFVCUYVXJNEANYQC
GGYFPXND4VBCROZZXTKAP7Y4JOP2OOYQAFVLMUE7SLFM225EUSIAC
EAGIDXOLFTHZMZ77ZWAM7MVVUBBXJMZD7RZUNBHJPYRFGGKILGVAC
6CR2EFUN7JXFHCBTNX3WWOOP4WFOCFO6KSPEBN6V6J5HFZO2LHNQC
FNNW5IEAXQ43WKB6QSQB7DFLG3Y3T5FYPXIUX7KQ2URR2GU3QLTAC
FRFFQV7VNYKGCA7ZAOSRPC2HHYTAIZ6AGGR7A5QEV6QPAQGFDYGAC
QXUEMZ3B2FUHFUC7ZZHJMH5FVWLEMEYXUMFA6JNXTJKIVZNMRIOAC
Q7FXTHVUPVAFMNY277C3NFJO3VXLZU5G6C6UYSD5QPURHSG3A7OQC
7L5LODGZ7AN4ZULDJZMLALD7PL6E57VZSNNSG67SFJARUJGCT47QC
OPFG6CZ26PPTGTH7ULLRQGZGR3YEIEJOV5W2E3WN7PFRZS62CVLQC
package com.github.jonathanxd.dracon.util
import java.io.File
import java.nio.file.Files
import java.nio.file.Path
import java.nio.file.Paths
fun findBinary(app: String): String {
// Works in Windows, Linux and MacOS when app is installed with cargo.
val localPijul = findCargo(app)
// For Homebrew/Linuxbrew installations
val brewPijul = findBrew(app)
// For *nix only.
val usrBinPijul = Paths.get(File.pathSeparator, "usr", "bin", app).asExecutableStringOrNull()
val usrLocalBinPijul = Paths.get(File.pathSeparator, "usr", "local", "bin", app).asExecutableStringOrNull()
val binPijul = Paths.get(File.pathSeparator, "bin", app).asExecutableStringOrNull()
// For Windows only.
// Windows could download Pijul binaries from https://github.com/boringcactus/pijul-windows-builds/releases/latest
// However, Dracon plugin could not detect Pijul outside from these directories, for the cases where
// Pijul is not in these locations, a search through $PATH variable will be made, so if the $PATH is correctly
// configured to point to Pijul executable, then it will be found, this applies to all OSes.
val programFiles = Paths.get("C:"+ File.pathSeparator, "Program Files", app, "$app.exe").asExecutableStringOrNull()
val programFiles86 = Paths.get("C:"+ File.pathSeparator, "Program Files (x86)", app, "$app.exe").asExecutableStringOrNull()
return localPijul
?: brewPijul
?: usrBinPijul
?: usrLocalBinPijul
?: binPijul
?: programFiles
?: programFiles86
?: findExecutableOnPath(app)
?: app
}
private fun Path.asExecutableStringOrNull(): String? =
this.existsOrNull()?.isRegularFileOrNull()?.isExecutableOrNull()?.toAbsolutePath()?.toString()
private fun findCargo(app: String): String? {
return System.getProperty("user.dir")?.let {
Paths.get(it, ".cargo", "bin", app)
}?.asExecutableStringOrNull()
}
private fun findBrew(app: String): String? {
return System.getenv("HOMEBREW_PREFIX")?.ifBlank { null }?.ifEmpty { null }?.let {
Paths.get(it, "bin", app)
}?.asExecutableStringOrNull()
}
fun findExecutableOnPath(name: String): String? {
for (dirname in System.getenv("PATH").split(File.pathSeparator)) {
val path = Paths.get(dirname, name)
if (Files.isRegularFile(path) && Files.isExecutable(path)) {
return path.toAbsolutePath().toString()
}
}
return null
}
// TODO: Should we limit to show only one revision when introspecting directories instead of all revisions?
val deleted = it.hunks.filterIsInstance<HunkWithPath>().filter {
it.resolvePath(ctx.root) == path
}.filterIsInstance<FileDelHunk>().isNotEmpty()
partner.acceptRevision(
PijulVcsFileRevision(
this.project,
root.toNioPath(),
pathToView,
PijulRevisionNumber(it.changeHash, it.date),
it.authors.map { VcsUserImpl(it.name ?: "", it.email ?: "") }.filter { it.name.isNotEmpty() },
it.message,
currentChannel,
deleted
)
)
/*// TODO: Should we limit to show only one revision when introspecting directories instead of all revisions?
package com.github.jonathanxd.dracon.editor
import com.github.jonathanxd.dracon.util.findBinary
import java.net.ServerSocket
import java.net.URI
import java.net.http.HttpClient
import java.net.http.HttpRequest
import java.net.http.HttpResponse
import java.time.Duration
import java.time.Instant
class EditorServer(val port: Int, val isClosed: () -> Boolean) {
private var connected: Boolean = false
private val client = HttpClient.newHttpClient()
private lateinit var content: String
fun connectAndRetrieveContent() {
val start = Instant.now()
while (!this.isClosed()) {
if (Duration.between(start, Instant.now()).seconds > 10) {
throw IllegalStateException("Could not connect to editor server! Timeout after 10 seconds.")
}
try {
val response = this.client.send(
HttpRequest.newBuilder(URI.create("http://0.0.0.0:$port/read"))
.GET()
.version(HttpClient.Version.HTTP_2)
.build(),
HttpResponse.BodyHandlers.ofString()
)
if (response.statusCode() == 200) {
this.connected = true
this.content = response.body();
break;
}
} catch (t: Throwable) {
}
Thread.sleep(200)
}
}
fun content() = this.content
fun connected() = this.connected
fun write(text: String) {
if (this.isClosed()) {
throw IllegalStateException("Could not write: editor-server is already closed.")
}
if (!this.connected) {
throw IllegalStateException("Could not write: Not connected to editor-server!")
}
val response = this.client.send(
HttpRequest.newBuilder(URI.create("http://0.0.0.0:$port/write"))
.POST(HttpRequest.BodyPublishers.ofString(text))
.version(HttpClient.Version.HTTP_2)
.build(),
HttpResponse.BodyHandlers.ofString()
)
handleResponse(response)
}
private fun handleResponse(response: HttpResponse<*>) {
if (response.statusCode() != 200) {
throw IllegalStateException("editor-server responded with $response.")
}
}
fun close() {
if (this.isClosed()) {
throw IllegalStateException("Could not close: editor-server is already closed.")
}
if (!this.connected) {
throw IllegalStateException("Could not close: Not connected to editor-server!")
}
try {
// This will make the connection close immediately
// thus throwing an exception.
this.client.send(
HttpRequest.newBuilder(URI.create("http://0.0.0.0:$port/close"))
.GET()
.version(HttpClient.Version.HTTP_2)
.build(),
HttpResponse.BodyHandlers.ofString()
)
} catch (t: Throwable) {
}
}
}
fun freePort(): Int = ServerSocket(0).use {
it.localPort
}
fun editorServerPath(): String = findBinary("editor-server")
val record = pijul(this.project).recordFromString(this.project, this.vcsRoot, this.editor.text)
this.editorServer.write(this.editor.text)
this.editorServer.close()
/*val record = pijul(this.project).recordFromString(this.project, this.vcsRoot, this.editor.text)
import com.github.jonathanxd.dracon.util.existsOrNull
import com.github.jonathanxd.dracon.util.isExecutableOrNull
import com.github.jonathanxd.dracon.util.isRegularFileOrNull
import com.github.jonathanxd.dracon.util.linesToFlow
import com.github.jonathanxd.dracon.util.*
}
}
override fun record(
project: Project,
root: Path,
editorServerConsumer: (EditorServer) -> Unit
): PijulOperationResult<String> {
val arguments = mutableListOf("record")
val operation = this.createPainlessExecPijulWithEditorServer(
this.project,
root,
arguments,
editorServerConsumer
)
return this.doExecutionWithMapper("record", operation) {
it
// For Homebrew/Linuxbrew installations
val brewPijul = this.findBrewPijul()
/**
* 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.
*
*/
@RequiresBackgroundThread
private fun createPainlessExecPijulWithEditorServer(project: Project,
dir: Path,
args: List<String>,
editorServerConsumer: (EditorServer) -> Unit): PijulExecution {
val freePort = freePort()
val process = ProcessBuilder()
.apply {
environment()["EDITOR_SERVER_PORT"] = freePort.toString()
environment()["VISUAL"] = editorServerPath()
environment()["EDITOR"] = editorServerPath()
}
.command(listOf(this.findPijul()) + args)
.directory(dir.toFile())
.start()
// For *nix only.
val usrBinPijul = Paths.get(File.pathSeparator, "usr", "bin", "pijul").asPijulExecutableStringOrNull()
val usrLocalBinPijul = Paths.get(File.pathSeparator, "usr", "local", "bin", "pijul").asPijulExecutableStringOrNull()
val binPijul = Paths.get(File.pathSeparator, "bin", "pijul").asPijulExecutableStringOrNull()
editorServerConsumer(EditorServer(freePort) {
!process.isAlive
})
// For Windows only.
// Windows could download Pijul binaries from https://github.com/boringcactus/pijul-windows-builds/releases/latest
// However, Dracon plugin could not detect Pijul outside from these directories, for the cases where
// Pijul is not in these locations, a search through $PATH variable will be made, so if the $PATH is correctly
// configured to point to Pijul executable, then it will be found, this applies to all OSes.
val programFiles = Paths.get("C:"+ File.pathSeparator, "Program Files", "pijul", "pijul.exe").asPijulExecutableStringOrNull()
val programFiles86 = Paths.get("C:"+ File.pathSeparator, "Program Files (x86)", "pijul", "pijul.exe").asPijulExecutableStringOrNull()
val input = process.inputStream
val error = process.errorStream
private fun Path.asPijulExecutableStringOrNull(): String? =
this.existsOrNull()?.isRegularFileOrNull()?.isExecutableOrNull()?.toAbsolutePath()?.toString()
if (exit == 0) {
draconConsoleWriter(project).logCommand("pijul", args, "<Exit status> $exit")
} else {
draconConsoleWriter(project).logCommandError("pijul", args, "<Exit status> $exit")
}
fun findExecutableOnPath(name: String): String? {
for (dirname in System.getenv("PATH").split(File.pathSeparator)) {
val path = Paths.get(dirname, name)
if (Files.isRegularFile(path) && Files.isExecutable(path)) {
return path.toAbsolutePath().toString()
}
}
private fun findPijul(): String = findBinary("pijul")
private fun getChanges(settings: ChangeBrowserSettings,
location: RepositoryLocation,
maxCount: Int,
consumer: (PijulCommittedChangeList) -> Unit) {
private fun getChanges(
settings: ChangeBrowserSettings,
maxCount: Int,
revision: VcsRevisionNumber? = null,
consumer: (PijulCommittedChangeList) -> Unit
) {
val rev = pijul(this.project).log(this.project, ctx.root).result?.entries?.filter {
it.revision.hash == number.asString()
}?.map { entry ->
PijulCommittedChangeList(
entry.message,
"",
entry.authors.firstOrNull()?.name ?: "No author",
Date.from(entry.date.toInstant()),
entry.hunks.filterIsInstance<HunkWithPath>().map { hunk ->
val beforeRevision =
if (hunk is FileAddHunk) null
else findARevisionBefore(entry.changeHash, allRevisions)?.let {
PijulContentRevision(
ctx.root,
hunk.resolvePath(ctx.root),
it,
this.project
)
}
this.getChanges(ChangeBrowserSettings(), 1, number) {
changes.add(it)
}
val afterRevision =
if (hunk is FileDelHunk) null
else PijulContentRevision(
ctx.root,
hunk.resolvePath(ctx.root),
entry.revision,
this.project
)
Change(
beforeRevision,
afterRevision,
hunk.status
)
}.toMutableList(),
entry.revision,
false,
pijulVcs(this.project)
)
}?.firstOrNull() ?: return null
val rev = changes.firstOrNull() ?: return null
if (recordString.result != null) {
if (record.statusCode is SuccessStatusCode) {
val result = record.result!!
val hash = result.substring(result.indexOf("Hash:") + "Hash:".length).trim()
Notifications.Bus.notify(
Notification(
EXPERT_MODE_GROUP,
DraconBundle.message("expert.mode.notification.title"),
DraconBundle.message("expert.mode.notification.success", hash),
NotificationType.ERROR,
),
project
)
} else {
record.statusCode as NonZeroExitStatusCode
Notifications.Bus.notify(
Notification(
EXPERT_MODE_GROUP,
DraconBundle.message("expert.mode.notification.title"),
DraconBundle.message("expert.mode.notification.failure", record.statusCode.exitCode, record.statusCode.message),
NotificationType.INFORMATION,
),
project
)
}
/*if (recordString.result != null) {