7L5LODGZ7AN4ZULDJZMLALD7PL6E57VZSNNSG67SFJARUJGCT47QC
message = 'Initial CahngeProvider'
timestamp = '2021-03-29T01:50:38.537749028Z'
[[authors]]
name = 'Jonathan H. R. Lopes'
# Dependencies
[2] PGNTR2EPCZBOWI67LKY6AN5B3RGIEOQ6NTTXGODLESKDSPWV26KQC
[3]+FNNW5IEAXQ43WKB6QSQB7DFLG3Y3T5FYPXIUX7KQ2URR2GU3QLTAC
[4]+OPFG6CZ26PPTGTH7ULLRQGZGR3YEIEJOV5W2E3WN7PFRZS62CVLQC
[5]+GGYFPXND4VBCROZZXTKAP7Y4JOP2OOYQAFVLMUE7SLFM225EUSIAC
[*] NTRPUMVQHUIQZ6O72NJ72XFYTZWZOSDA6CSKMUCGKFVNE3KIDYYQC
# Hunks
1. File addition: "revision" in "src/main/kotlin/com/github/jonathanxd/dracon" +dx
up 3.107, new 0:10
2. File addition: "PijulRevisionNumber.kt" in "src/main/kotlin/com/github/jonathanxd/dracon/revision"
up 0.11, new 12:36
+ package com.github.jonathanxd.dracon.revision
+
+ import com.intellij.openapi.vcs.changes.patch.BlobIndexUtil
+ import com.intellij.openapi.vcs.history.ShortVcsRevisionNumber
+ import com.intellij.openapi.vcs.history.VcsRevisionNumber
+ import java.time.LocalDateTime
+ import java.time.ZonedDateTime
+
+ class PijulRevisionNumber(val hash: String, val timestamp: ZonedDateTime) : ShortVcsRevisionNumber {
+
+ val NOT_COMMITTED_HASH = BlobIndexUtil.NOT_COMMITTED_HASH
+
+ override fun compareTo(other: VcsRevisionNumber): Int {
+ if (this === other) return 0
+
+ if (other is PijulRevisionNumber) {
+ return this.timestamp.compareTo(other.timestamp)
+ }
+
+ return -1
+ }
+
+ override fun asString(): String = this.hash
+
+ override fun toShortString(): String = this.asString()
+ }
\
3. File addition: "provider" in "src/main/kotlin/com/github/jonathanxd/dracon" +dx
up 3.107, new 843:853
4. File addition: "PijulChangeProvider.kt" in "src/main/kotlin/com/github/jonathanxd/dracon/provider"
up 0.854, new 855:879
+ package com.github.jonathanxd.dracon.provider
+
+ import com.intellij.openapi.progress.ProgressIndicator
+ import com.intellij.openapi.project.Project
+ import com.intellij.openapi.vcs.VcsKey
+ import com.intellij.openapi.vcs.changes.*
+
+ class PijulChangeProvider(val project: Project, val key: VcsKey) : ChangeProvider {
+ override fun getChanges(
+ dirtyScope: VcsDirtyScope,
+ builder: ChangelistBuilder,
+ progress: ProgressIndicator,
+ addGate: ChangeListManagerGate
+ ) {
+ if (project.isDisposed) return
+ val dirtDirs = dirtyScope.recursivelyDirtyDirectories
+
+ println(dirtDirs)
+ }
+
+ override fun isModifiedDocumentTrackingRequired(): Boolean = true
+ }
\
5. Edit in src/main/kotlin/com/github/jonathanxd/dracon/pijul/Pijul.kt:13 3.4428
up 3.4473, new 1584:1698, down 3.4473
+ import com.github.jonathanxd.dracon.log.PijulLog
+ import com.github.jonathanxd.dracon.revision.PijulRevisionNumber
6. Edit in src/main/kotlin/com/github/jonathanxd/dracon/pijul/Pijul.kt:82 3.4428
up 3.6839, new 1699:1950, down 3.6839
+ @RequiresBackgroundThread
+ fun latestRevisionNumber(project: Project, root: VirtualFile): PijulOperationResult<PijulRevisionNumber>
+
+ @RequiresBackgroundThread
+ fun log(project: Project, root: VirtualFile): PijulOperationResult<PijulLog>
7. File addition: "PijulLog.kt" in "src/main/kotlin/com/github/jonathanxd/dracon/log"
up 3.6847, new 1951:1964
+ package com.github.jonathanxd.dracon.log
+
+ import java.time.LocalDateTime
+ import java.time.ZonedDateTime
+ import java.time.format.DateTimeFormatter
+ import java.time.format.ResolverStyle
+
+
+
+
+
+
+
+ class Author(val name: String?, val fullName: String?, val email: String?)
+ class PijulLog(val entries: List<PijulLogEntry>)
+
+ class PijulLogEntry(
+ val changeHash: String,
+ val message: String,
+ val date: ZonedDateTime,
+ val authors: List<Author>
+ )
+
+
+ val MESSAGE_PATTERN = Regex("message = '(.*)'\\n")
+ val TIME_PATTERN = Regex("timestamp = '(.*)'\\n")
+ val AUTHORS_SECTION_PATTERN = Regex("\\[\\[authors\\]\\]\n")
+ val AUTHOR_PATTERN = Regex("name = '(.*)'\\n")
+
+ fun String.parseChange(hash: String): PijulLogEntry {
+ val message = MESSAGE_PATTERN.find(this)?.groupValues?.get(1)!!
+ val timestamp = TIME_PATTERN.find(this)?.groupValues?.get(1)!!
+ val authorsSection = AUTHORS_SECTION_PATTERN.find(this)?.range?.endInclusive
+ val authors = mutableListOf<Author>()
+
+ if (authorsSection != null) {
+
+ var lastFound: Int? = authorsSection
+ while (lastFound != null) {
+ val foundAuthor = AUTHOR_PATTERN.find(this, lastFound)
+ val foundAuthorGroup = foundAuthor?.groupValues?.get(1)
+
+ if (foundAuthorGroup != null) {
+ authors += Author(foundAuthorGroup, null, null)
+ lastFound = if (foundAuthor.range.last + 1 < this.length && this[foundAuthor.range.last + 1] == '\n') {
+ null
+ } else {
+ foundAuthor.range.last
+ }
+ } else {
+ lastFound = null
+ }
+ }
+ }
+
+ return PijulLogEntry(hash, message, timestamp.parseAsLocalDateTime(), authors)
+ }
+
+ val RFC3339_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss[.SSSSSSSSS]XXX")
+ .withResolverStyle(ResolverStyle.LENIENT)
+
+ fun String.parseAsLocalDateTime(): ZonedDateTime {
+ return ZonedDateTime.parse(this, RFC3339_FORMATTER)
+ }
\
8. Edit in src/main/kotlin/com/github/jonathanxd/dracon/cmd/PijulCmd.kt:13 3.10972
up 3.11015, new 4000:4103, down 3.11015
+ import com.github.jonathanxd.dracon.log.PijulLog
+ import com.github.jonathanxd.dracon.log.PijulLogEntry
9. Edit in src/main/kotlin/com/github/jonathanxd/dracon/cmd/PijulCmd.kt:16 3.10972
up 3.11075, new 4104:4156, down 3.11075
+ import com.github.jonathanxd.dracon.log.parseChange
10. Edit in src/main/kotlin/com/github/jonathanxd/dracon/cmd/PijulCmd.kt:20 3.10972
up 4.2779, new 4157:4222, down 3.11119
+ import com.github.jonathanxd.dracon.revision.PijulRevisionNumber
11. Edit in src/main/kotlin/com/github/jonathanxd/dracon/cmd/PijulCmd.kt:106 3.10972
up 3.13610, new 4223:4490, down 3.13610
+
+ override fun log(project: Project, root: VirtualFile): PijulOperationResult<PijulLog> {
+ val rootPath = Paths.get(VcsUtil.getFilePath(root).path)
+
+ val logHashExecution = this.execPijul(project, rootPath, listOf("log", "--hash-only"), delay = 10L)
12. Edit in src/main/kotlin/com/github/jonathanxd/dracon/cmd/PijulCmd.kt:112 3.10972
up 3.13611, new 4491:5952, down 3.13611
+ val hashes = this.doExecutionWithMapper("log--hash-only", logHashExecution) {
+ it.lines()
+ }
+
+ if (hashes.statusCode !is SuccessStatusCode) {
+ return hashes as PijulOperationResult<PijulLog>
+ } else {
+ val entries = mutableListOf<PijulLogEntry>()
+ for (hash in hashes.result!!) {
+ val change = this.doExecutionWithMapper("change-$hash", this.execPijul(project, rootPath, listOf("change", hash), delay = 10L)) {
+ it.parseChange(hash)
+ }
+
+ if (change.statusCode !is SuccessStatusCode) {
+ return change as PijulOperationResult<PijulLog>
+ }
+ }
+
+ return PijulOperationResult(hashes.operation, hashes.statusCode, PijulLog(entries))
+ }
+ }
+
+ override fun latestRevisionNumber(project: Project, root: VirtualFile): PijulOperationResult<PijulRevisionNumber> {
+ /*val root = ProjectLevelVcsManager.getInstance(project).getVcsRootFor(file)
+ ?: return PijulOperationResult("file_status", SuccessStatusCode, FileStatus.UNKNOWN)*/
+ val rootPath = Paths.get(VcsUtil.getFilePath(root).path)
+
+ val log = this.log(project, root)
+
+ return PijulOperationResult(log.operation, log.statusCode,
+ log.result?.entries?.firstOrNull()?.let {
+ PijulRevisionNumber(it.changeHash, it.date)
+ }
+ )
+ }
+
13. Edit in src/main/kotlin/com/github/jonathanxd/dracon/PijulVcs.kt:14 3.24146
up 3.24239, new 5953:6018, down 3.24239
+ import com.github.jonathanxd.dracon.provider.PijulChangeProvider
14. Edit in src/main/kotlin/com/github/jonathanxd/dracon/PijulVcs.kt:20 3.24146
up 3.24475, new 6019:6074, down 3.24475
+ import com.intellij.openapi.vcs.changes.ChangeProvider
15. Edit in src/main/kotlin/com/github/jonathanxd/dracon/PijulVcs.kt:46 3.24146
up 3.25153, new 6075:6178, down 3.25153
+ override fun getChangeProvider(): ChangeProvider =
+ PijulChangeProvider(this.project, KEY)
+
16. File addition: "dada.toml" in "/"
up 1.0, new 6179:6190
17. Edit in build.gradle.kts:33 5.13712
up 5.14803, new 6193:6231, down 5.14803
+ maven(url = "https://jitpack.io")
18. Edit in build.gradle.kts:36 5.13712
up 5.14820, new 6232:6394, down 5.14820
+ implementation("org.tomlj:tomlj:1.0.0")
+ implementation("com.github.JonathanxD.JwIUtils:JwIUtils:4.17.0") {
+ exclude(group = "org.jetbrains")
+ }
19. Replacement in README.md:21 5.17946
B:BD 2.59651 -> 2.59651:59660/2
up 2.59651, new 6395:8239
- - [ ] ...
+ - [ ] ...
+
+
+ # FAQ
+
+ ## Why Dracon and not Pijul for IDEA (or something like that)?
+
+ Because Dracon is not an official plugin, and I want to make sure that it is clear to anyone who uses this plugin. Dracon is an individual project being developed by [Jonathan H. R. Lopes](https://github.com/JonathanxD) which does not have any relation with Pijul creator. Also, Dracon uses MIT license, allowing to anyone, and that includes the Pijul creator, to contribute and create your own version of Dracon, if wish to.
+
+ ## Why Dracon is developed against IntelliJ EAP instead of a stable releases?
+
+ Dracon is being developed based in actual [HG4Idea](https://github.com/JetBrains/intellij-community/tree/master/plugins/hg4idea) and [Git4Idea](https://github.com/JetBrains/intellij-community/tree/master/plugins/git4idea) plugins, as well based in current code of [IntelliJ IDEA Community](https://github.com/JetBrains/intellij-community/), and is planned to be released by the end of first semester of 2021, when release time comes, the IntelliJ EAP will be already released as stable, also I don't want to support older releases of IntelliJ IDEA because this involves basing on deprecated code, and this is my first IDEA plugin, so the more I focus the present than the past, more I deliver in terms of better and stable features.
+
+ ## Could I contribute to Dracon development?
+
+ I'm currently focused on learning how IDEA Plugin platform works and how DVCS is implemented in IDEA, so until Dracon is officially released, I will not be accepting contributions, but Dracon is licensed under MIT, you are free to create your own version of Dracon and developing it on your own.
+
+ ## When Dracon will be official released?
+
+ I will be periodically releasing Dragon experimental builds, but Dracon stable release is planned by the end of first semester of 2021.
\
package com.github.jonathanxd.dracon.test
import com.github.jonathanxd.dracon.log.*
import io.kotest.core.spec.style.ShouldSpec
import io.kotest.matchers.shouldBe
import java.time.ZoneOffset
import java.time.ZonedDateTime
class DraconTest : ShouldSpec({
should("correctly parse a 'pijul change' text without authors") {
val text = """
message = 'Initial ChangeProvider'
timestamp = '2021-03-29T01:50:38.537749028Z'
""".trimIndent()
val change = text.parseChange("DRACON")
change.changeHash shouldBe "DRACON"
change.message shouldBe "Initial ChangeProvider"
change.date shouldBe ZonedDateTime.of(2021, 3, 29, 1, 50, 38, 537749028, ZoneOffset.UTC)
}
should("correctly parse a 'pijul change' text with single author") {
val text = """
message = 'Initial ChangeProvider'
timestamp = '2021-03-29T01:50:38.537749028Z'
[[authors]]
name = 'Author'
""".trimIndent()
val change = text.parseChange("DRACON")
change.changeHash shouldBe "DRACON"
change.message shouldBe "Initial ChangeProvider"
change.authors.isEmpty() shouldBe false
change.authors[0].name shouldBe "Author"
change.date shouldBe ZonedDateTime.of(2021, 3, 29, 1, 50, 38, 537749028, ZoneOffset.UTC)
}
should("correctly parse a 'pijul change' text with two authors") {
val text = """
message = 'Initial ChangeProvider'
timestamp = '2021-03-29T01:50:38.537749028Z'
[[authors]]
name = 'Author1'
name = 'Author2'
""".trimIndent()
val change = text.parseChange("DRACON")
change.changeHash shouldBe "DRACON"
change.message shouldBe "Initial ChangeProvider"
change.authors.size shouldBe 2
change.authors[0].name shouldBe "Author1"
change.authors[1].name shouldBe "Author2"
change.date shouldBe ZonedDateTime.of(2021, 3, 29, 1, 50, 38, 537749028, ZoneOffset.UTC)
}
should("correctly parse a 'pijul change' text with three authors and dependencies") {
val text = """
message = 'Initial ChangeProvider'
timestamp = '2021-03-29T01:50:38.537749028Z'
[[authors]]
name = 'Author1'
name = 'Author2'
name = 'Author3'
# Dependencies
[2] PGNTR2EPCZBOWI67LKY6AN5B3RGIEOQ6NTTXGODLESKDSPWV26KQC
[3]+FNNW5IEAXQ43WKB6QSQB7DFLG3Y3T5FYPXIUX7KQ2URR2GU3QLTAC
[4]+OPFG6CZ26PPTGTH7ULLRQGZGR3YEIEJOV5W2E3WN7PFRZS62CVLQC
[5]+GGYFPXND4VBCROZZXTKAP7Y4JOP2OOYQAFVLMUE7SLFM225EUSIAC
[*] NTRPUMVQHUIQZ6O72NJ72XFYTZWZOSDA6CSKMUCGKFVNE3KIDYYQC
""".trimIndent()
val change = text.parseChange("DRACON")
change.changeHash shouldBe "DRACON"
change.message shouldBe "Initial ChangeProvider"
change.authors.size shouldBe 3
change.authors[0].name shouldBe "Author1"
change.authors[1].name shouldBe "Author2"
change.authors[2].name shouldBe "Author3"
change.date shouldBe ZonedDateTime.of(2021, 3, 29, 1, 50, 38, 537749028, ZoneOffset.UTC)
change.dependencies.size shouldBe 5
change.dependencies[0] shouldBe Dependency(
IndexInt(2),
DependencyType.REGULAR_DEPENDENCY,
"PGNTR2EPCZBOWI67LKY6AN5B3RGIEOQ6NTTXGODLESKDSPWV26KQC"
)
change.dependencies[1] shouldBe Dependency(
IndexInt(3),
DependencyType.CHANGE_DEPENDENCY,
"FNNW5IEAXQ43WKB6QSQB7DFLG3Y3T5FYPXIUX7KQ2URR2GU3QLTAC"
)
change.dependencies[2] shouldBe Dependency(
IndexInt(4),
DependencyType.CHANGE_DEPENDENCY,
"OPFG6CZ26PPTGTH7ULLRQGZGR3YEIEJOV5W2E3WN7PFRZS62CVLQC"
)
change.dependencies[3] shouldBe Dependency(
IndexInt(5),
DependencyType.CHANGE_DEPENDENCY,
"GGYFPXND4VBCROZZXTKAP7Y4JOP2OOYQAFVLMUE7SLFM225EUSIAC"
)
change.dependencies[4] shouldBe Dependency(
Asterisk,
DependencyType.EXTRA_KNOWN_DEPENDENCY,
"NTRPUMVQHUIQZ6O72NJ72XFYTZWZOSDA6CSKMUCGKFVNE3KIDYYQC"
)
}
should("correctly parse a 'pijul change' text with example change") {
val text = """
message = 'Initial ChangeProvider'
timestamp = '2021-03-29T01:50:38.537749028Z'
[[authors]]
name = 'Author1'
# Dependencies
[2] PGNTR2EPCZBOWI67LKY6AN5B3RGIEOQ6NTTXGODLESKDSPWV26KQC
[3]+FNNW5IEAXQ43WKB6QSQB7DFLG3Y3T5FYPXIUX7KQ2URR2GU3QLTAC
[4]+OPFG6CZ26PPTGTH7ULLRQGZGR3YEIEJOV5W2E3WN7PFRZS62CVLQC
[5]+GGYFPXND4VBCROZZXTKAP7Y4JOP2OOYQAFVLMUE7SLFM225EUSIAC
[*] NTRPUMVQHUIQZ6O72NJ72XFYTZWZOSDA6CSKMUCGKFVNE3KIDYYQC
# Hunks
1. File addition: "revision" in "src/main/kotlin/com/github/jonathanxd/dracon" +dx
up 3.107, new 0:10
2. File addition: "PijulRevisionNumber.kt" in "src/main/kotlin/com/github/jonathanxd/dracon/revision"
up 0.11, new 12:36
+ package com.github.jonathanxd.dracon.revision
+
+ import com.intellij.openapi.vcs.changes.patch.BlobIndexUtil
+ import com.intellij.openapi.vcs.history.ShortVcsRevisionNumber
+ import com.intellij.openapi.vcs.history.VcsRevisionNumber
+ import java.time.LocalDateTime
+ import java.time.ZonedDateTime
+
+ class PijulRevisionNumber(val hash: String, val timestamp: ZonedDateTime) : ShortVcsRevisionNumber {
+
+ val NOT_COMMITTED_HASH = BlobIndexUtil.NOT_COMMITTED_HASH
+
+ override fun compareTo(other: VcsRevisionNumber): Int {
+ if (this === other) return 0
+
+ if (other is PijulRevisionNumber) {
+ return this.timestamp.compareTo(other.timestamp)
+ }
+
+ return -1
+ }
+
+ override fun asString(): String = this.hash
+
+ override fun toShortString(): String = this.asString()
+ }
\
3. File addition: "provider" in "src/main/kotlin/com/github/jonathanxd/dracon" +dx
up 3.107, new 843:853
4. File addition: "PijulChangeProvider.kt" in "src/main/kotlin/com/github/jonathanxd/dracon/provider"
up 0.854, new 855:879
+ package com.github.jonathanxd.dracon.provider
+
+ import com.intellij.openapi.progress.ProgressIndicator
+ import com.intellij.openapi.project.Project
+ import com.intellij.openapi.vcs.VcsKey
+ import com.intellij.openapi.vcs.changes.*
+
+ class PijulChangeProvider(val project: Project, val key: VcsKey) : ChangeProvider {
+ override fun getChanges(
+ dirtyScope: VcsDirtyScope,
+ builder: ChangelistBuilder,
+ progress: ProgressIndicator,
+ addGate: ChangeListManagerGate
+ ) {
+ if (project.isDisposed) return
+ val dirtDirs = dirtyScope.recursivelyDirtyDirectories
+
+ println(dirtDirs)
+ }
+
+ override fun isModifiedDocumentTrackingRequired(): Boolean = true
+ }
\
5. Edit in src/main/kotlin/com/github/jonathanxd/dracon/pijul/Pijul.kt:13 3.4428
up 3.4473, new 1584:1698, down 3.4473
+ import com.github.jonathanxd.dracon.log.PijulLog
+ import com.github.jonathanxd.dracon.revision.PijulRevisionNumber
6. Edit in src/main/kotlin/com/github/jonathanxd/dracon/pijul/Pijul.kt:82 3.4428
up 3.6839, new 1699:1950, down 3.6839
+ @RequiresBackgroundThread
+ fun latestRevisionNumber(project: Project, root: VirtualFile): PijulOperationResult<PijulRevisionNumber>
+
+ @RequiresBackgroundThread
+ fun log(project: Project, root: VirtualFile): PijulOperationResult<PijulLog>
7. File addition: "PijulLog.kt" in "src/main/kotlin/com/github/jonathanxd/dracon/log"
up 3.6847, new 1951:1964
+ package com.github.jonathanxd.dracon.log
+
+ import java.time.LocalDateTime
+ import java.time.ZonedDateTime
+ import java.time.format.DateTimeFormatter
+ import java.time.format.ResolverStyle
+
+
+
+
+
+
+
+ class Author(val name: String?, val fullName: String?, val email: String?)
+ class PijulLog(val entries: List<PijulLogEntry>)
+
+ class PijulLogEntry(
+ val changeHash: String,
+ val message: String,
+ val date: ZonedDateTime,
+ val authors: List<Author>
+ )
+
+
+ val MESSAGE_PATTERN = Regex("message = '(.*)'\\n")
+ val TIME_PATTERN = Regex("timestamp = '(.*)'\\n")
+ val AUTHORS_SECTION_PATTERN = Regex("\\[\\[authors\\]\\]\n")
+ val AUTHOR_PATTERN = Regex("name = '(.*)'\\n")
+
+ fun String.parseChange(hash: String): PijulLogEntry {
+ val message = MESSAGE_PATTERN.find(this)?.groupValues?.get(1)!!
+ val timestamp = TIME_PATTERN.find(this)?.groupValues?.get(1)!!
+ val authorsSection = AUTHORS_SECTION_PATTERN.find(this)?.range?.endInclusive
+ val authors = mutableListOf<Author>()
+
+ if (authorsSection != null) {
+
+ var lastFound: Int? = authorsSection
+ while (lastFound != null) {
+ val foundAuthor = AUTHOR_PATTERN.find(this, lastFound)
+ val foundAuthorGroup = foundAuthor?.groupValues?.get(1)
+
+ if (foundAuthorGroup != null) {
+ authors += Author(foundAuthorGroup, null, null)
+ lastFound = if (foundAuthor.range.last + 1 < this.length && this[foundAuthor.range.last + 1] == '\n') {
+ null
+ } else {
+ foundAuthor.range.last
+ }
+ } else {
+ lastFound = null
+ }
+ }
+ }
+
+ return PijulLogEntry(hash, message, timestamp.parseAsLocalDateTime(), authors)
+ }
+
+ val RFC3339_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss[.SSSSSSSSS]XXX")
+ .withResolverStyle(ResolverStyle.LENIENT)
+
+ fun String.parseAsLocalDateTime(): ZonedDateTime {
+ return ZonedDateTime.parse(this, RFC3339_FORMATTER)
+ }
\
8. Edit in src/main/kotlin/com/github/jonathanxd/dracon/cmd/PijulCmd.kt:13 3.10972
up 3.11015, new 4000:4103, down 3.11015
+ import com.github.jonathanxd.dracon.log.PijulLog
+ import com.github.jonathanxd.dracon.log.PijulLogEntry
9. Edit in src/main/kotlin/com/github/jonathanxd/dracon/cmd/PijulCmd.kt:16 3.10972
up 3.11075, new 4104:4156, down 3.11075
+ import com.github.jonathanxd.dracon.log.parseChange
10. Edit in src/main/kotlin/com/github/jonathanxd/dracon/cmd/PijulCmd.kt:20 3.10972
up 4.2779, new 4157:4222, down 3.11119
+ import com.github.jonathanxd.dracon.revision.PijulRevisionNumber
11. Edit in src/main/kotlin/com/github/jonathanxd/dracon/cmd/PijulCmd.kt:106 3.10972
up 3.13610, new 4223:4490, down 3.13610
+
+ override fun log(project: Project, root: VirtualFile): PijulOperationResult<PijulLog> {
+ val rootPath = Paths.get(VcsUtil.getFilePath(root).path)
+
+ val logHashExecution = this.execPijul(project, rootPath, listOf("log", "--hash-only"), delay = 10L)
12. Edit in src/main/kotlin/com/github/jonathanxd/dracon/cmd/PijulCmd.kt:112 3.10972
up 3.13611, new 4491:5952, down 3.13611
+ val hashes = this.doExecutionWithMapper("log--hash-only", logHashExecution) {
+ it.lines()
+ }
+
+ if (hashes.statusCode !is SuccessStatusCode) {
+ return hashes as PijulOperationResult<PijulLog>
+ } else {
+ val entries = mutableListOf<PijulLogEntry>()
+ for (hash in hashes.result!!) {
+ val change = this.doExecutionWithMapper("change-""" + "\$hash" + """, this.execPijul(project, rootPath, listOf("change", hash), delay = 10L)) {
+ it.parseChange(hash)
+ }
+
+ if (change.statusCode !is SuccessStatusCode) {
+ return change as PijulOperationResult<PijulLog>
+ }
+ }
+
+ return PijulOperationResult(hashes.operation, hashes.statusCode, PijulLog(entries))
+ }
+ }
+
+ override fun latestRevisionNumber(project: Project, root: VirtualFile): PijulOperationResult<PijulRevisionNumber> {
+ /*val root = ProjectLevelVcsManager.getInstance(project).getVcsRootFor(file)
+ ?: return PijulOperationResult("file_status", SuccessStatusCode, FileStatus.UNKNOWN)*/
+ val rootPath = Paths.get(VcsUtil.getFilePath(root).path)
+
+ val log = this.log(project, root)
+
+ return PijulOperationResult(log.operation, log.statusCode,
+ log.result?.entries?.firstOrNull()?.let {
+ PijulRevisionNumber(it.changeHash, it.date)
+ }
+ )
+ }
+
13. Edit in src/main/kotlin/com/github/jonathanxd/dracon/PijulVcs.kt:14 3.24146
up 3.24239, new 5953:6018, down 3.24239
+ import com.github.jonathanxd.dracon.provider.PijulChangeProvider
14. Edit in src/main/kotlin/com/github/jonathanxd/dracon/PijulVcs.kt:20 3.24146
up 3.24475, new 6019:6074, down 3.24475
+ import com.intellij.openapi.vcs.changes.ChangeProvider
15. Edit in src/main/kotlin/com/github/jonathanxd/dracon/PijulVcs.kt:46 3.24146
up 3.25153, new 6075:6178, down 3.25153
+ override fun getChangeProvider(): ChangeProvider =
+ PijulChangeProvider(this.project, KEY)
+
16. File addition: "dada.toml" in "/"
up 1.0, new 6179:6190
17. Edit in build.gradle.kts:33 5.13712
up 5.14803, new 6193:6231, down 5.14803
+ maven(url = "https://jitpack.io")
18. Edit in build.gradle.kts:36 5.13712
up 5.14820, new 6232:6394, down 5.14820
+ implementation("org.tomlj:tomlj:1.0.0")
+ implementation("com.github.JonathanxD.JwIUtils:JwIUtils:4.17.0") {
+ exclude(group = "org.jetbrains")
+ }
19. Replacement in README.md:21 5.17946
B:BD 2.59651 -> 2.59651:59660/2
up 2.59651, new 6395:8239
- - [ ] ...
+ - [ ] ...
+
+
+ # FAQ
+
+ ## Why Dracon and not Pijul for IDEA (or something like that)?
+
+ Because Dracon is not an official plugin, and I want to make sure that it is clear to anyone who uses this plugin. Dracon is an individual project being developed by [Jonathan H. R. Lopes](https://github.com/JonathanxD) which does not have any relation with Pijul creator. Also, Dracon uses MIT license, allowing to anyone, and that includes the Pijul creator, to contribute and create your own version of Dracon, if wish to.
+
+ ## Why Dracon is developed against IntelliJ EAP instead of a stable releases?
+
+ Dracon is being developed based in actual [HG4Idea](https://github.com/JetBrains/intellij-community/tree/master/plugins/hg4idea) and [Git4Idea](https://github.com/JetBrains/intellij-community/tree/master/plugins/git4idea) plugins, as well based in current code of [IntelliJ IDEA Community](https://github.com/JetBrains/intellij-community/), and is planned to be released by the end of first semester of 2021, when release time comes, the IntelliJ EAP will be already released as stable, also I don't want to support older releases of IntelliJ IDEA because this involves basing on deprecated code, and this is my first IDEA plugin, so the more I focus the present than the past, more I deliver in terms of better and stable features.
+
+ ## Could I contribute to Dracon development?
+
+ I'm currently focused on learning how IDEA Plugin platform works and how DVCS is implemented in IDEA, so until Dracon is officially released, I will not be accepting contributions, but Dracon is licensed under MIT, you are free to create your own version of Dracon and developing it on your own.
+
+ ## When Dracon will be official released?
+
+ I will be periodically releasing Dragon experimental builds, but Dracon stable release is planned by the end of first semester of 2021.
\
""".trimIndent()
val change = text.parseChange("DRACON")
change.changeHash shouldBe "DRACON"
change.message shouldBe "Initial ChangeProvider"
change.authors.size shouldBe 1
change.authors[0].name shouldBe "Author1"
change.date shouldBe ZonedDateTime.of(2021, 3, 29, 1, 50, 38, 537749028, ZoneOffset.UTC)
change.dependencies.size shouldBe 5
change.dependencies[0] shouldBe Dependency(
IndexInt(2),
DependencyType.REGULAR_DEPENDENCY,
"PGNTR2EPCZBOWI67LKY6AN5B3RGIEOQ6NTTXGODLESKDSPWV26KQC"
)
change.dependencies[1] shouldBe Dependency(
IndexInt(3),
DependencyType.CHANGE_DEPENDENCY,
"FNNW5IEAXQ43WKB6QSQB7DFLG3Y3T5FYPXIUX7KQ2URR2GU3QLTAC"
)
change.dependencies[2] shouldBe Dependency(
IndexInt(4),
DependencyType.CHANGE_DEPENDENCY,
"OPFG6CZ26PPTGTH7ULLRQGZGR3YEIEJOV5W2E3WN7PFRZS62CVLQC"
)
change.dependencies[3] shouldBe Dependency(
IndexInt(5),
DependencyType.CHANGE_DEPENDENCY,
"GGYFPXND4VBCROZZXTKAP7Y4JOP2OOYQAFVLMUE7SLFM225EUSIAC"
)
change.dependencies[4] shouldBe Dependency(
Asterisk,
DependencyType.EXTRA_KNOWN_DEPENDENCY,
"NTRPUMVQHUIQZ6O72NJ72XFYTZWZOSDA6CSKMUCGKFVNE3KIDYYQC"
)
change.hunks.size shouldBe 19
val sources = change.hunks.map {
when (it) {
is FileAddHunk -> it.fileAddHunkAsSource()
is EditHunk -> it.editHunkAsSource()
is ReplacementHunk -> it.replacementHunkAsSource()
else -> it.toString()
}
}
println(sources)
change.hunks[0] shouldBe FileAddHunk(
hunkNumber = 1,
type = HunkType.FILE_ADDITION,
path = "revision",
rootPath = "src/main/kotlin/com/github/jonathanxd/dracon",
lines = emptyList(),
meta = Meta("+dx"),
flags = emptyList(),
context = Context(up = listOf(3.107), new = IntRange(0, 10), down = emptyList()),
)
change.hunks[1] shouldBe FileAddHunk(
hunkNumber = 2,
type = HunkType.FILE_ADDITION,
path = "PijulRevisionNumber.kt",
rootPath = "src/main/kotlin/com/github/jonathanxd/dracon/revision",
lines = listOf(
LineChange(type = LineChangeType.ADD, data = "package com.github.jonathanxd.dracon.revision"),
LineChange(type = LineChangeType.ADD, data = ""),
LineChange(
type = LineChangeType.ADD,
data = "import com.intellij.openapi.vcs.changes.patch.BlobIndexUtil"
),
LineChange(
type = LineChangeType.ADD,
data = "import com.intellij.openapi.vcs.history.ShortVcsRevisionNumber"
),
LineChange(
type = LineChangeType.ADD,
data = "import com.intellij.openapi.vcs.history.VcsRevisionNumber"
),
LineChange(type = LineChangeType.ADD, data = "import java.time.LocalDateTime"),
LineChange(type = LineChangeType.ADD, data = "import java.time.ZonedDateTime"),
LineChange(type = LineChangeType.ADD, data = ""),
LineChange(
type = LineChangeType.ADD,
data = "class PijulRevisionNumber(val hash: String, val timestamp: ZonedDateTime) : ShortVcsRevisionNumber {"
),
LineChange(type = LineChangeType.ADD, data = ""),
LineChange(
type = LineChangeType.ADD,
data = " val NOT_COMMITTED_HASH = BlobIndexUtil.NOT_COMMITTED_HASH"
),
LineChange(type = LineChangeType.ADD, data = ""),
LineChange(
type = LineChangeType.ADD,
data = " override fun compareTo(other: VcsRevisionNumber): Int {"
),
LineChange(type = LineChangeType.ADD, data = " if (this === other) return 0"),
LineChange(type = LineChangeType.ADD, data = ""),
LineChange(type = LineChangeType.ADD, data = " if (other is PijulRevisionNumber) {"),
LineChange(
type = LineChangeType.ADD,
data = " return this.timestamp.compareTo(other.timestamp)"
),
LineChange(type = LineChangeType.ADD, data = " }"),
LineChange(type = LineChangeType.ADD, data = ""),
LineChange(type = LineChangeType.ADD, data = " return -1"),
LineChange(type = LineChangeType.ADD, data = " }"),
LineChange(type = LineChangeType.ADD, data = ""),
LineChange(type = LineChangeType.ADD, data = " override fun asString(): String = this.hash"),
LineChange(type = LineChangeType.ADD, data = ""),
LineChange(
type = LineChangeType.ADD,
data = " override fun toShortString(): String = this.asString()"
),
LineChange(type = LineChangeType.ADD, data = "}")
),
meta = Meta(""),
flags = emptyList(),
context = Context(up = listOf(0.11), new = IntRange(12, 36), down = emptyList()),
)
change.hunks[2] shouldBe FileAddHunk(
hunkNumber = 3,
type = HunkType.FILE_ADDITION,
path = "provider",
rootPath = "src/main/kotlin/com/github/jonathanxd/dracon",
lines = emptyList(),
meta = Meta("+dx"),
flags = emptyList(),
context = Context(up = listOf(3.107), new = IntRange(843, 853), down = emptyList()),
)
change.hunks[3] shouldBe FileAddHunk(
hunkNumber = 4,
type = HunkType.FILE_ADDITION,
path = "PijulChangeProvider.kt",
rootPath = "src/main/kotlin/com/github/jonathanxd/dracon/provider",
lines = listOf(
LineChange(type = LineChangeType.ADD, data = "package com.github.jonathanxd.dracon.provider"),
LineChange(type = LineChangeType.ADD, data = ""),
LineChange(type = LineChangeType.ADD, data = "import com.intellij.openapi.progress.ProgressIndicator"),
LineChange(type = LineChangeType.ADD, data = "import com.intellij.openapi.project.Project"),
LineChange(type = LineChangeType.ADD, data = "import com.intellij.openapi.vcs.VcsKey"),
LineChange(type = LineChangeType.ADD, data = "import com.intellij.openapi.vcs.changes.*"),
LineChange(type = LineChangeType.ADD, data = ""),
LineChange(
type = LineChangeType.ADD,
data = "class PijulChangeProvider(val project: Project, val key: VcsKey) : ChangeProvider {"
),
LineChange(type = LineChangeType.ADD, data = " override fun getChanges("),
LineChange(type = LineChangeType.ADD, data = " dirtyScope: VcsDirtyScope,"),
LineChange(type = LineChangeType.ADD, data = " builder: ChangelistBuilder,"),
LineChange(type = LineChangeType.ADD, data = " progress: ProgressIndicator,"),
LineChange(type = LineChangeType.ADD, data = " addGate: ChangeListManagerGate"),
LineChange(type = LineChangeType.ADD, data = " ) {"),
LineChange(type = LineChangeType.ADD, data = " if (project.isDisposed) return"),
LineChange(
type = LineChangeType.ADD,
data = " val dirtDirs = dirtyScope.recursivelyDirtyDirectories"
),
LineChange(type = LineChangeType.ADD, data = ""),
LineChange(type = LineChangeType.ADD, data = " println(dirtDirs)"),
LineChange(type = LineChangeType.ADD, data = " }"),
LineChange(type = LineChangeType.ADD, data = ""),
LineChange(
type = LineChangeType.ADD,
data = " override fun isModifiedDocumentTrackingRequired(): Boolean = true"
),
LineChange(type = LineChangeType.ADD, data = "}")
),
meta = Meta(""),
flags = emptyList(),
context = Context(up = listOf(0.854), new = IntRange(855, 879), down = emptyList()),
)
change.hunks[4] shouldBe EditHunk(
hunkNumber = 5,
type = HunkType.FILE_EDIT,
path = "src/main/kotlin/com/github/jonathanxd/dracon/pijul/Pijul.kt",
startLine = 13,
inode = 3.4428,
lines = listOf(
LineChange(
type = LineChangeType.ADD,
data = "import com.github.jonathanxd.dracon.log.PijulLog"
),
LineChange(
type = LineChangeType.ADD,
data = "import com.github.jonathanxd.dracon.revision.PijulRevisionNumber"
)
),
meta = null,
flags = emptyList(),
context = Context(up = listOf(3.4473), new = IntRange(1584, 1698), down = listOf(3.4473)),
)
change.hunks[5] shouldBe EditHunk(
hunkNumber = 6,
type = HunkType.FILE_EDIT,
path = "src/main/kotlin/com/github/jonathanxd/dracon/pijul/Pijul.kt",
startLine = 82,
inode = 3.4428,
lines = listOf(
LineChange(type = LineChangeType.ADD, data = " @RequiresBackgroundThread"),
LineChange(
type = LineChangeType.ADD,
data = " fun latestRevisionNumber(project: Project, root: VirtualFile): PijulOperationResult<PijulRevisionNumber>"
),
LineChange(type = LineChangeType.ADD, data = ""),
LineChange(type = LineChangeType.ADD, data = " @RequiresBackgroundThread"),
LineChange(
type = LineChangeType.ADD,
data = " fun log(project: Project, root: VirtualFile): PijulOperationResult<PijulLog>"
)
),
meta = null,
flags = emptyList(),
context = Context(up = listOf(3.6839), new = IntRange(1699, 1950), down = listOf(3.6839)),
)
change.hunks[6] shouldBe FileAddHunk(
hunkNumber = 7,
type = HunkType.FILE_ADDITION,
path = "PijulLog.kt",
rootPath = "src/main/kotlin/com/github/jonathanxd/dracon/log",
lines = listOf(
LineChange(type = LineChangeType.ADD, data = "package com.github.jonathanxd.dracon.log"),
LineChange(type = LineChangeType.ADD, data = ""),
LineChange(type = LineChangeType.ADD, data = "import java.time.LocalDateTime"),
LineChange(type = LineChangeType.ADD, data = "import java.time.ZonedDateTime"),
LineChange(type = LineChangeType.ADD, data = "import java.time.format.DateTimeFormatter"),
LineChange(type = LineChangeType.ADD, data = "import java.time.format.ResolverStyle"),
LineChange(type = LineChangeType.ADD, data = ""),
LineChange(type = LineChangeType.ADD, data = ""),
LineChange(type = LineChangeType.ADD, data = ""),
LineChange(type = LineChangeType.ADD, data = ""),
LineChange(type = LineChangeType.ADD, data = ""),
LineChange(type = LineChangeType.ADD, data = ""),
LineChange(type = LineChangeType.ADD, data = ""),
LineChange(
type = LineChangeType.ADD,
data = "class Author(val name: String?, val fullName: String?, val email: String?)"
),
LineChange(type = LineChangeType.ADD, data = "class PijulLog(val entries: List<PijulLogEntry>)"),
LineChange(type = LineChangeType.ADD, data = ""),
LineChange(type = LineChangeType.ADD, data = "class PijulLogEntry("),
LineChange(type = LineChangeType.ADD, data = " val changeHash: String,"),
LineChange(type = LineChangeType.ADD, data = " val message: String,"),
LineChange(type = LineChangeType.ADD, data = " val date: ZonedDateTime,"),
LineChange(type = LineChangeType.ADD, data = " val authors: List<Author>"),
LineChange(type = LineChangeType.ADD, data = ")"),
LineChange(type = LineChangeType.ADD, data = ""),
LineChange(type = LineChangeType.ADD, data = ""),
LineChange(type = LineChangeType.ADD, data = "val MESSAGE_PATTERN = Regex(\"message = '(.*)'\\\\n\")"),
LineChange(type = LineChangeType.ADD, data = "val TIME_PATTERN = Regex(\"timestamp = '(.*)'\\\\n\")"),
LineChange(
type = LineChangeType.ADD,
data = "val AUTHORS_SECTION_PATTERN = Regex(\"\\\\[\\\\[authors\\\\]\\\\]\\n\")"
),
LineChange(type = LineChangeType.ADD, data = "val AUTHOR_PATTERN = Regex(\"name = '(.*)'\\\\n\")"),
LineChange(type = LineChangeType.ADD, data = ""),
LineChange(type = LineChangeType.ADD, data = "fun String.parseChange(hash: String): PijulLogEntry {"),
LineChange(
type = LineChangeType.ADD,
data = " val message = MESSAGE_PATTERN.find(this)?.groupValues?.get(1)!!"
),
LineChange(
type = LineChangeType.ADD,
data = " val timestamp = TIME_PATTERN.find(this)?.groupValues?.get(1)!!"
),
LineChange(
type = LineChangeType.ADD,
data = " val authorsSection = AUTHORS_SECTION_PATTERN.find(this)?.range?.endInclusive"
),
LineChange(type = LineChangeType.ADD, data = " val authors = mutableListOf<Author>()"),
LineChange(type = LineChangeType.ADD, data = ""),
LineChange(type = LineChangeType.ADD, data = " if (authorsSection != null) {"),
LineChange(type = LineChangeType.ADD, data = ""),
LineChange(type = LineChangeType.ADD, data = " var lastFound: Int? = authorsSection"),
LineChange(type = LineChangeType.ADD, data = " while (lastFound != null) {"),
LineChange(
type = LineChangeType.ADD,
data = " val foundAuthor = AUTHOR_PATTERN.find(this, lastFound)"
),
LineChange(
type = LineChangeType.ADD,
data = " val foundAuthorGroup = foundAuthor?.groupValues?.get(1)"
),
LineChange(type = LineChangeType.ADD, data = ""),
LineChange(type = LineChangeType.ADD, data = " if (foundAuthorGroup != null) {"),
LineChange(
type = LineChangeType.ADD,
data = " authors += Author(foundAuthorGroup, null, null)"
),
LineChange(
type = LineChangeType.ADD,
data = " lastFound = if (foundAuthor.range.last + 1 < this.length && this[foundAuthor.range.last + 1] == '\\n') {"
),
LineChange(type = LineChangeType.ADD, data = " null"),
LineChange(type = LineChangeType.ADD, data = " } else {"),
LineChange(type = LineChangeType.ADD, data = " foundAuthor.range.last"),
LineChange(type = LineChangeType.ADD, data = " }"),
LineChange(type = LineChangeType.ADD, data = " } else {"),
LineChange(type = LineChangeType.ADD, data = " lastFound = null"),
LineChange(type = LineChangeType.ADD, data = " }"),
LineChange(type = LineChangeType.ADD, data = " }"),
LineChange(type = LineChangeType.ADD, data = " }"),
LineChange(type = LineChangeType.ADD, data = ""),
LineChange(
type = LineChangeType.ADD,
data = " return PijulLogEntry(hash, message, timestamp.parseAsLocalDateTime(), authors)"
),
LineChange(type = LineChangeType.ADD, data = "}"),
LineChange(type = LineChangeType.ADD, data = ""),
LineChange(
type = LineChangeType.ADD,
data = "val RFC3339_FORMATTER = DateTimeFormatter.ofPattern(\"yyyy-MM-dd'T'HH:mm:ss[.SSSSSSSSS]XXX\")"
),
LineChange(type = LineChangeType.ADD, data = " .withResolverStyle(ResolverStyle.LENIENT)"),
LineChange(type = LineChangeType.ADD, data = ""),
LineChange(type = LineChangeType.ADD, data = "fun String.parseAsLocalDateTime(): ZonedDateTime {"),
LineChange(type = LineChangeType.ADD, data = " return ZonedDateTime.parse(this, RFC3339_FORMATTER)"),
LineChange(type = LineChangeType.ADD, data = "}")
),
meta = Meta(""),
flags = emptyList(),
context = Context(up = listOf(3.6847), new = IntRange(1951, 1964), down = emptyList()),
)
change.hunks[7] shouldBe EditHunk(
hunkNumber = 8,
type = HunkType.FILE_EDIT,
path = "src/main/kotlin/com/github/jonathanxd/dracon/cmd/PijulCmd.kt",
startLine = 13,
inode = 3.10972,
lines = listOf(
LineChange(
type = LineChangeType.ADD,
data = "import com.github.jonathanxd.dracon.log.PijulLog"
), LineChange(type = LineChangeType.ADD, data = "import com.github.jonathanxd.dracon.log.PijulLogEntry")
),
meta = null,
flags = emptyList(),
context = Context(up = listOf(3.11015), new = IntRange(4000, 4103), down = listOf(3.11015)),
)
change.hunks[8] shouldBe EditHunk(
hunkNumber = 9,
type = HunkType.FILE_EDIT,
path = "src/main/kotlin/com/github/jonathanxd/dracon/cmd/PijulCmd.kt",
startLine = 16,
inode = 3.10972,
lines = listOf(
LineChange(
type = LineChangeType.ADD,
data = "import com.github.jonathanxd.dracon.log.parseChange"
)
),
meta = null,
flags = emptyList(),
context = Context(up = listOf(3.11075), new = IntRange(4104, 4156), down = listOf(3.11075)),
)
change.hunks[9] shouldBe EditHunk(
hunkNumber = 10,
type = HunkType.FILE_EDIT,
path = "src/main/kotlin/com/github/jonathanxd/dracon/cmd/PijulCmd.kt",
startLine = 20,
inode = 3.10972,
lines = listOf(
LineChange(
type = LineChangeType.ADD,
data = "import com.github.jonathanxd.dracon.revision.PijulRevisionNumber"
)
),
meta = null,
flags = emptyList(),
context = Context(up = listOf(4.2779), new = IntRange(4157, 4222), down = listOf(3.11119)),
)
change.hunks[10] shouldBe EditHunk(
hunkNumber = 11,
type = HunkType.FILE_EDIT,
path = "src/main/kotlin/com/github/jonathanxd/dracon/cmd/PijulCmd.kt",
startLine = 106,
inode = 3.10972,
lines = listOf(
LineChange(type = LineChangeType.ADD, data = ""),
LineChange(
type = LineChangeType.ADD,
data = " override fun log(project: Project, root: VirtualFile): PijulOperationResult<PijulLog> {"
),
LineChange(
type = LineChangeType.ADD,
data = " val rootPath = Paths.get(VcsUtil.getFilePath(root).path)"
),
LineChange(type = LineChangeType.ADD, data = ""),
LineChange(
type = LineChangeType.ADD,
data = " val logHashExecution = this.execPijul(project, rootPath, listOf(\"log\", \"--hash-only\"), delay = 10L)"
)
),
meta = null,
flags = emptyList(),
context = Context(up = listOf(3.1361), new = IntRange(4223, 4490), down = listOf(3.1361)),
)
change.hunks[11] shouldBe EditHunk(
hunkNumber = 12,
type = HunkType.FILE_EDIT,
path = "src/main/kotlin/com/github/jonathanxd/dracon/cmd/PijulCmd.kt",
startLine = 112,
inode = 3.10972,
lines = listOf(
LineChange(
type = LineChangeType.ADD,
data = " val hashes = this.doExecutionWithMapper(\"log--hash-only\", logHashExecution) {"
),
LineChange(type = LineChangeType.ADD, data = " it.lines()"),
LineChange(type = LineChangeType.ADD, data = " }"),
LineChange(type = LineChangeType.ADD, data = ""),
LineChange(type = LineChangeType.ADD, data = " if (hashes.statusCode !is SuccessStatusCode) {"),
LineChange(
type = LineChangeType.ADD,
data = " return hashes as PijulOperationResult<PijulLog>"
),
LineChange(type = LineChangeType.ADD, data = " } else {"),
LineChange(
type = LineChangeType.ADD,
data = " val entries = mutableListOf<PijulLogEntry>()"
),
LineChange(type = LineChangeType.ADD, data = " for (hash in hashes.result!!) {"),
LineChange(
type = LineChangeType.ADD,
data = " val change = this.doExecutionWithMapper(\"change-\$hash, this.execPijul(project, rootPath, listOf(\"change\", hash), delay = 10L)) {"
),
LineChange(type = LineChangeType.ADD, data = " it.parseChange(hash)"),
LineChange(type = LineChangeType.ADD, data = " }"),
LineChange(type = LineChangeType.ADD, data = ""),
LineChange(
type = LineChangeType.ADD,
data = " if (change.statusCode !is SuccessStatusCode) {"
),
LineChange(
type = LineChangeType.ADD,
data = " return change as PijulOperationResult<PijulLog>"
),
LineChange(type = LineChangeType.ADD, data = " }"),
LineChange(type = LineChangeType.ADD, data = " }"),
LineChange(type = LineChangeType.ADD, data = ""),
LineChange(
type = LineChangeType.ADD,
data = " return PijulOperationResult(hashes.operation, hashes.statusCode, PijulLog(entries))"
),
LineChange(type = LineChangeType.ADD, data = " }"),
LineChange(type = LineChangeType.ADD, data = " }"),
LineChange(type = LineChangeType.ADD, data = ""),
LineChange(
type = LineChangeType.ADD,
data = " override fun latestRevisionNumber(project: Project, root: VirtualFile): PijulOperationResult<PijulRevisionNumber> {"
),
LineChange(
type = LineChangeType.ADD,
data = " /*val root = ProjectLevelVcsManager.getInstance(project).getVcsRootFor(file)"
),
LineChange(
type = LineChangeType.ADD,
data = " ?: return PijulOperationResult(\"file_status\", SuccessStatusCode, FileStatus.UNKNOWN)*/"
),
LineChange(
type = LineChangeType.ADD,
data = " val rootPath = Paths.get(VcsUtil.getFilePath(root).path)"
),
LineChange(type = LineChangeType.ADD, data = ""),
LineChange(type = LineChangeType.ADD, data = " val log = this.log(project, root)"),
LineChange(type = LineChangeType.ADD, data = ""),
LineChange(
type = LineChangeType.ADD,
data = " return PijulOperationResult(log.operation, log.statusCode,"
),
LineChange(type = LineChangeType.ADD, data = " log.result?.entries?.firstOrNull()?.let {"),
LineChange(
type = LineChangeType.ADD,
data = " PijulRevisionNumber(it.changeHash, it.date)"
),
LineChange(type = LineChangeType.ADD, data = " }"),
LineChange(type = LineChangeType.ADD, data = " )"),
LineChange(type = LineChangeType.ADD, data = " }"),
LineChange(type = LineChangeType.ADD, data = "")
),
meta = null,
flags = emptyList(),
context = Context(up = listOf(3.13611), new = IntRange(4491, 5952), down = listOf(3.13611)),
)
change.hunks[12] shouldBe EditHunk(
hunkNumber = 13,
type = HunkType.FILE_EDIT,
path = "src/main/kotlin/com/github/jonathanxd/dracon/PijulVcs.kt",
startLine = 14,
inode = 3.24146,
lines = listOf(
LineChange(
type = LineChangeType.ADD,
data = "import com.github.jonathanxd.dracon.provider.PijulChangeProvider"
)
),
meta = null,
flags = emptyList(),
context = Context(up = listOf(3.24239), new = IntRange(5953, 6018), down = listOf(3.24239)),
)
change.hunks[13] shouldBe EditHunk(
hunkNumber = 14,
type = HunkType.FILE_EDIT,
path = "src/main/kotlin/com/github/jonathanxd/dracon/PijulVcs.kt",
startLine = 20,
inode = 3.24146,
lines = listOf(
LineChange(
type = LineChangeType.ADD,
data = "import com.intellij.openapi.vcs.changes.ChangeProvider"
)
),
meta = null,
flags = emptyList(),
context = Context(up = listOf(3.24475), new = IntRange(6019, 6074), down = listOf(3.24475)),
)
change.hunks[14] shouldBe EditHunk(
hunkNumber = 15,
type = HunkType.FILE_EDIT,
path = "src/main/kotlin/com/github/jonathanxd/dracon/PijulVcs.kt",
startLine = 46,
inode = 3.24146,
lines = listOf(
LineChange(
type = LineChangeType.ADD,
data = " override fun getChangeProvider(): ChangeProvider ="
),
LineChange(type = LineChangeType.ADD, data = " PijulChangeProvider(this.project, KEY)"),
LineChange(type = LineChangeType.ADD, data = "")
),
meta = null,
flags = emptyList(),
context = Context(up = listOf(3.25153), new = IntRange(6075, 6178), down = listOf(3.25153)),
)
change.hunks[15] shouldBe FileAddHunk(
hunkNumber = 16,
type = HunkType.FILE_ADDITION,
path = "dada.toml",
rootPath = "/",
lines = emptyList(),
meta = Meta(""),
flags = emptyList(),
context = Context(up = listOf(1.0), new = IntRange(6179, 6190), down = emptyList()),
)
change.hunks[16] shouldBe EditHunk(
hunkNumber = 17,
type = HunkType.FILE_EDIT,
path = "build.gradle.kts",
startLine = 33,
inode = 5.13712,
lines = listOf(LineChange(type = LineChangeType.ADD, data = " maven(url = \"https://jitpack.io\")")),
meta = null,
flags = emptyList(),
context = Context(up = listOf(5.14803), new = IntRange(6193, 6231), down = listOf(5.14803)),
)
change.hunks[17] shouldBe EditHunk(
hunkNumber = 18,
type = HunkType.FILE_EDIT,
path = "build.gradle.kts",
startLine = 36,
inode = 5.13712,
lines = listOf(
LineChange(
type = LineChangeType.ADD,
data = " implementation(\"org.tomlj:tomlj:1.0.0\")"
),
LineChange(
type = LineChangeType.ADD,
data = " implementation(\"com.github.JonathanxD.JwIUtils:JwIUtils:4.17.0\") {"
),
LineChange(type = LineChangeType.ADD, data = " exclude(group = \"org.jetbrains\")"),
LineChange(type = LineChangeType.ADD, data = " }")
),
meta = null,
flags = emptyList(),
context = Context(up = listOf(5.1482), new = IntRange(6232, 6394), down = listOf(5.1482)),
)
change.hunks[18] shouldBe ReplacementHunk(
hunkNumber = 19,
type = HunkType.REPLACEMENT,
path = "README.md",
startLine = 21,
inode = 5.17946,
lines = listOf(
LineChange(type = LineChangeType.DEL, data = "- [ ] ..."),
LineChange(type = LineChangeType.ADD, data = "- [ ] ..."),
LineChange(type = LineChangeType.ADD, data = ""),
LineChange(type = LineChangeType.ADD, data = ""),
LineChange(type = LineChangeType.ADD, data = "# FAQ"),
LineChange(type = LineChangeType.ADD, data = ""),
LineChange(
type = LineChangeType.ADD,
data = "## Why Dracon and not Pijul for IDEA (or something like that)?"
),
LineChange(type = LineChangeType.ADD, data = ""),
LineChange(
type = LineChangeType.ADD,
data = "Because Dracon is not an official plugin, and I want to make sure that it is clear to anyone who uses this plugin. Dracon is an individual project being developed by [Jonathan H. R. Lopes](https://github.com/JonathanxD) which does not have any relation with Pijul creator. Also, Dracon uses MIT license, allowing to anyone, and that includes the Pijul creator, to contribute and create your own version of Dracon, if wish to."
),
LineChange(type = LineChangeType.ADD, data = ""),
LineChange(
type = LineChangeType.ADD,
data = "## Why Dracon is developed against IntelliJ EAP instead of a stable releases?"
),
LineChange(type = LineChangeType.ADD, data = ""),
LineChange(
type = LineChangeType.ADD,
data = "Dracon is being developed based in actual [HG4Idea](https://github.com/JetBrains/intellij-community/tree/master/plugins/hg4idea) and [Git4Idea](https://github.com/JetBrains/intellij-community/tree/master/plugins/git4idea) plugins, as well based in current code of [IntelliJ IDEA Community](https://github.com/JetBrains/intellij-community/), and is planned to be released by the end of first semester of 2021, when release time comes, the IntelliJ EAP will be already released as stable, also I don't want to support older releases of IntelliJ IDEA because this involves basing on deprecated code, and this is my first IDEA plugin, so the more I focus the present than the past, more I deliver in terms of better and stable features."
),
LineChange(type = LineChangeType.ADD, data = ""),
LineChange(type = LineChangeType.ADD, data = "## Could I contribute to Dracon development?"),
LineChange(type = LineChangeType.ADD, data = ""),
LineChange(
type = LineChangeType.ADD,
data = "I'm currently focused on learning how IDEA Plugin platform works and how DVCS is implemented in IDEA, so until Dracon is officially released, I will not be accepting contributions, but Dracon is licensed under MIT, you are free to create your own version of Dracon and developing it on your own."
),
LineChange(type = LineChangeType.ADD, data = ""),
LineChange(type = LineChangeType.ADD, data = "## When Dracon will be official released?"),
LineChange(type = LineChangeType.ADD, data = ""),
LineChange(
type = LineChangeType.ADD,
data = "I will be periodically releasing Dragon experimental builds, but Dracon stable release is planned by the end of first semester of 2021."
)
),
meta = null,
flags = emptyList(),
context = Context(up = listOf(2.59651), new = IntRange(6395, 8239), down = emptyList()),
)
}
})
class Author(val name: String?, val fullName: String?, val email: String?)
class PijulLog(val entries: List<PijulLogEntry>)
/**
* Only appears in dependencies of [extra-known type][DependencyType.EXTRA_KNOWN_DEPENDENCY].
*/
object Asterisk : IntOrAsterisk() {
override fun toString(): String = "*"
}
/**
* Appears in dependencies of [regular type][DependencyType.REGULAR_DEPENDENCY] and [change type][DependencyType.CHANGE_DEPENDENCY].
*/
data class IndexInt(val index: Int) : IntOrAsterisk()
enum class DependencyType {
/**
* Regular dependency.
*/
REGULAR_DEPENDENCY,
/**
* Change dependency.
*/
CHANGE_DEPENDENCY,
/**
* Extra known "context" changes.
*/
EXTRA_KNOWN_DEPENDENCY,
/**
* Unknown dependency type.
*/
UNKNOWN
}
// Hunk
sealed class Hunk {
abstract val hunkNumber: Int
abstract val type: HunkType
abstract val context: Context?
abstract val meta: Meta?
abstract val flags: List<Flag>
}
data class FileAddHunk(
override val hunkNumber: Int,
override val type: HunkType = HunkType.FILE_ADDITION,
val path: String,
val rootPath: String,
val lines: List<LineChange>,
override val meta: Meta?,
override val flags: List<Flag> = emptyList(),
override val context: Context?
): Hunk()
data class FileDelHunk(
override val hunkNumber: Int,
override val type: HunkType = HunkType.FILE_DELETION,
val path: String,
val inode: Double?,
val lines: List<LineChange>,
override val meta: Meta?,
override val flags: List<Flag>,
override val context: Context?
): Hunk()
data class FileUnDelHunk(
override val hunkNumber: Int,
override val type: HunkType = HunkType.FILE_UN_DELETION,
val path: String,
val inode: Double?,
val lines: List<LineChange>,
override val meta: Meta?,
override val flags: List<Flag>,
override val context: Context?
): Hunk()
data class MoveHunk(
override val hunkNumber: Int,
override val type: HunkType = HunkType.FILE_MOVE,
val older: String,
val new: String,
override val meta: Meta?,
override val flags: List<Flag>,
override val context: Context?
): Hunk()
class PijulLogEntry(
val changeHash: String,
val message: String,
val date: ZonedDateTime,
val authors: List<Author>
data class EditHunk(
override val hunkNumber: Int,
override val type: HunkType = HunkType.FILE_EDIT,
val path: String,
val startLine: Int,
val inode: Double?,
val lines: List<LineChange>,
override val meta: Meta?,
override val flags: List<Flag>,
override val context: Context?
): Hunk()
data class ReplacementHunk(
override val hunkNumber: Int,
override val type: HunkType = HunkType.REPLACEMENT,
val path: String,
val startLine: Int,
val inode: Double?,
val lines: List<LineChange>,
override val meta: Meta?,
override val flags: List<Flag>,
override val context: Context?
): Hunk()
data class NameConflictHunk(
override val hunkNumber: Int,
override val type: HunkType,
val path: String,
val inode: Double?,
val deletedNames: List<String>,
override val meta: Meta?,
override val flags: List<Flag>,
override val context: Context?
): Hunk()
data class OrderConflictHunk(
override val hunkNumber: Int,
override val type: HunkType,
val path: String,
val index: Int?, // ??
val inode: Double?,
val lines: List<LineChange>,
override val meta: Meta?,
override val flags: List<Flag>,
override val context: Context?
): Hunk()
data class ZombieResurrectHunk(
override val hunkNumber: Int,
override val type: HunkType = HunkType.ZOMBIE_RESURRECT,
val path: String,
val line: Int,
val inode: Double?,
val lines: List<LineChange>,
override val meta: Meta?,
override val flags: List<Flag>,
override val context: Context?
): Hunk()
/**
* Meta is ignored as it is not needed for this plugin work.
*
* However, we still read metadata and store them, as they are useful to keep changelog reading consistent.
*/
data class Meta(val plain: String)
/**
* Flag is ignored as it is not needed for this plugin work.
*
* However, we still read flags and store them, as they are useful to keep changelog reading consistent.
*/
data class Flag(val plain: String)
data class Context(
val up: List<Double>,
val new: IntRange?,
val down: List<Double>
enum class HunkType {
FILE_ADDITION,
FILE_DELETION,
FILE_UN_DELETION,
FILE_MOVE,
FILE_EDIT,
REPLACEMENT,
SOLVING_NAME_CONFLICT,
UN_SOLVING_NAME_CONFLICT,
UNKNOWN_NAME_CONFLICT_RESOLUTION,
SOLVING_ORDER_CONFLICT,
UN_SOLVING_ORDER_CONFLICT,
UNKNOWN_ORDER_CONFLICT_RESOLUTION,
ZOMBIE_RESURRECT
}
data class LineChange(val type: LineChangeType, val data: String)
enum class LineChangeType(val symbol: String) {
ADD("+"),
DEL("-");
override fun toString(): String =
"${this.name}(${this.symbol})"
companion object {
fun fromSymbol(symbol: String): LineChangeType =
when (symbol) {
"+" -> ADD
"-" -> DEL
else -> throw IllegalArgumentException("Unknown change symbol: $symbol. Known symbols: +, -. For: ADD, DEL.")
}
}
}
val AUTHORS_SECTION_PATTERN = Regex("\\[\\[authors\\]\\]\n")
val AUTHOR_PATTERN = Regex("name = '(.*)'\\n")
val AUTHORS_SECTION_PATTERN = Regex("\\[\\[authors\\]\\]\\n")
val AUTHOR_PATTERN = Regex("name = '(.*)'\\n?")
val DEPENDENCIES_SECTION_PATTERN = Regex("# Dependencies\\n")
val DEPENDENCY_PATTERN = Regex("\\[(\\d+|\\*)\\]([+ ])([A-Z0-9]+)\\n?")
val HUNK_SECTION_PATTERN = Regex("# Hunks\\n")
val HUNK_ADD_PATTERN = Regex("(\\d+)\\. File addition: \"([^\"]*)\" in \"([^\"]*)\"( [^ ]+)?\\n?")
val HUNK_DEL_PATTERN = Regex("(\\d+)\\. File deletion: \"([^\"]*)\" (\\d+\\.\\d+)\\n?")
val HUNK_UNDEL_PATTERN = Regex("(\\d+)\\. File un-deletion: \"([^\"]*)\" (\\d+\\.\\d+)\\n?")
val HUNK_MOVE_PATTERN = Regex("(\\d+)\\. Moved: \"([^\"]*)\" \"([^\"]*)\" ([^ ]+ )?(.*)\\n?")
val HUNK_EDIT_PATTERN = Regex("(\\d+)\\. Edit in ([^:]+):(\\d+) (\\d+\\.\\d+)\\n?")
val HUNK_REPLACEMENT_PATTERN = Regex("(\\d+)\\. Replacement in ([^:]+):(\\d+) (\\d+\\.\\d+)\\n?")
val HUNK_NAME_CONFLICT_PATTERN = Regex("(\\d+)\\. (Solving|Un-solving) a name conflict in \"([^\"]*)\" (.*): (.*)\\n?")
val HUNK_ORDER_CONFLICT_PATTERN = Regex("(\\d+)\\. (Solving|Un-solving) an order conflict in (.*):(\\d+)? (\\d+\\.\\d+)\\n?")
val HUNK_ZOMBIE_PATTERN = Regex("(\\d+)\\. Resurrecting zombie lines in (\"[^\"]+\"):(\\d+) (\\d+\\.\\d+)\\n?")
val HUNK_CONTEXT = Regex("up ((\\d+\\.\\d+ )*\\d+\\.\\d+)(, new \\d+:\\d+)?(, down ((\\d+\\.\\d+ )*\\d+\\.\\d+))?\\n?")
val CHANGE_PATTERN = Regex("([-+]) (.*)\\n?")
/**
* Flags are currently ignored by Dracon, as they are not required for the plugin work correctly.
*/
val FLAG_PATTERN = Regex("([BFD]+:[BFD]+ [^\\n]+)\\n?")
}
} else {
lastFound = null
}
}
}
if (dependencySection != null) {
var lastFound: Int? = dependencySection
while (lastFound != null) {
val foundDependency = DEPENDENCY_PATTERN.find(this, lastFound)
if (foundDependency != null) {
val foundDependencyId = foundDependency.groupValues[1]
val foundDependencySeparator = foundDependency.groupValues[2]
val foundDependencyHash = foundDependency.groupValues[3]
val dependencyType = when {
foundDependencyId == "*" && foundDependencySeparator == " " -> DependencyType.EXTRA_KNOWN_DEPENDENCY
foundDependencyId.toIntOrNull() != null && foundDependencySeparator == "+" -> DependencyType.CHANGE_DEPENDENCY
foundDependencyId.toIntOrNull() != null && foundDependencySeparator == " " -> DependencyType.REGULAR_DEPENDENCY
else -> DependencyType.UNKNOWN
}
val intOrAsterisk = foundDependencyId.toIntOrNull()?.let(::IndexInt) ?: Asterisk
dependencies += Dependency(
intOrAsterisk,
dependencyType,
foundDependencyHash
)
lastFound = if (foundDependency.range.last.endsInNewLine(this)) {
null
} else {
foundDependency.range.last
if (hunkSection != null) {
var lastFound: Int? = hunkSection
while (lastFound != null) {
if (this[lastFound] == '\n') {
lastFound += 1
continue;
}
var anyFound = false
val add = HUNK_ADD_PATTERN.matchEntire(this.substringUntil(lastFound, '\n'))
if (add != null) {
anyFound = true
lastFound += add.range.last
val hunkNumber = add.groupValues[1].toInt()
val fileOrPath = add.groupValues[2]
val rootPath = add.groupValues[3]
val meta = add.groupValues.getOrNull(4)?.trim()?.let(::Meta)
val (context, stoppedLine) = this.tryReadContext(lastFound)
lastFound = stoppedLine
val (lines, lastKnownLine) = this.readLineChanges(lastFound)
lastFound = lastKnownLine
hunks += FileAddHunk(
hunkNumber = hunkNumber,
path = fileOrPath,
rootPath = rootPath,
lines = lines,
meta = meta,
context = context
)
}
val remove = HUNK_DEL_PATTERN.matchEntire(this.substringUntil(lastFound, '\n'))
if (remove != null) {
anyFound = true
lastFound += remove.range.last
val hunkNumber = remove.groupValues[1].toInt()
val path = remove.groupValues[2]
val inode = remove.groupValues[3].toDoubleOrNull()
val (flags, flagsLastKnownLine) = this.readFlags(lastFound)
lastFound = flagsLastKnownLine
val (lines, linesLastKnownLine) = this.readLineChanges(lastFound)
lastFound = linesLastKnownLine
hunks += FileDelHunk(
hunkNumber = hunkNumber,
path = path,
inode = inode,
flags = flags,
lines = lines,
meta = null,
context = null
)
}
val undel = HUNK_UNDEL_PATTERN.matchEntire(this.substringUntil(lastFound, '\n'))
if (undel != null) {
anyFound = true
lastFound += undel.range.last
val hunkNumber = undel.groupValues[1].toInt()
val path = undel.groupValues[2]
val inode = undel.groupValues[3].toDoubleOrNull()
val (flags, flagsLastKnownLine) = this.readFlags(lastFound)
lastFound = flagsLastKnownLine
val (lines, linesLastKnownLine) = this.readLineChanges(lastFound)
lastFound = linesLastKnownLine
hunks += FileUnDelHunk(
hunkNumber = hunkNumber,
path = path,
inode = inode,
flags = flags,
lines = lines,
meta = null,
context = null
)
}
val move = HUNK_MOVE_PATTERN.matchEntire(this.substringUntil(lastFound, '\n'))
if (move != null) {
anyFound = true
lastFound += move.range.last
val hunkNumber = move.groupValues[1].toInt()
val new = move.groupValues[2]
val old = move.groupValues[3]
val meta = move.groupValues.getOrNull(4)?.trim()?.let(::Meta)
val (flags, flagsLastKnownLine) = this.readFlags(lastFound)
lastFound = flagsLastKnownLine
val (context, stoppedLine) = this.tryReadContext(lastFound)
lastFound = stoppedLine
hunks += MoveHunk(
hunkNumber = hunkNumber,
older = old,
new = new,
flags = flags,
meta = meta,
context = context
)
}
return PijulLogEntry(hash, message, timestamp.parseAsLocalDateTime(), authors)
val edit = HUNK_EDIT_PATTERN.matchEntire(this.substringUntil(lastFound, '\n'))
if (edit != null) {
anyFound = true
lastFound += edit.range.last
val hunkNumber = edit.groupValues[1].toInt()
val path = edit.groupValues[2]
val line = edit.groupValues[3].toInt()
val inode = edit.groupValues.getOrNull(4)?.toDoubleOrNull()
val (flags, flagsLastKnownLine) = this.readFlags(lastFound)
lastFound = flagsLastKnownLine
val (context, stoppedLine) = this.tryReadContext(lastFound)
lastFound = stoppedLine
val (lines, linesLastKnownLine) = this.readLineChanges(lastFound)
lastFound = linesLastKnownLine
hunks += EditHunk(
hunkNumber = hunkNumber,
path = path,
startLine = line,
inode = inode,
flags = flags,
meta = null,
lines = lines,
context = context
)
}
val replacement = HUNK_REPLACEMENT_PATTERN.matchEntire(this.substringUntil(lastFound, '\n'))
if (replacement != null) {
anyFound = true
lastFound += replacement.range.last
val hunkNumber = replacement.groupValues[1].toInt()
val path = replacement.groupValues[2]
val line = replacement.groupValues[3].toInt()
val inode = replacement.groupValues.getOrNull(4)?.toDoubleOrNull()
val (flags, flagsLastKnownLine) = this.readFlags(lastFound)
lastFound = flagsLastKnownLine
val (context, stoppedLine) = this.tryReadContext(lastFound)
lastFound = stoppedLine
val (lines, linesLastKnownLine) = this.readLineChanges(lastFound)
lastFound = linesLastKnownLine
hunks += ReplacementHunk(
hunkNumber = hunkNumber,
path = path,
startLine = line,
inode = inode,
flags = flags,
meta = null,
lines = lines,
context = context
)
}
val nameConflict = HUNK_NAME_CONFLICT_PATTERN.matchEntire(this.substringUntil(lastFound, '\n'))
if (nameConflict != null) {
anyFound = true
lastFound += nameConflict.range.last
val hunkNumber = nameConflict.groupValues[1].toInt()
val typeStr = nameConflict.groupValues[2]
val path = nameConflict.groupValues[3]
val inode = nameConflict.groupValues[4].toDouble()
val deletedNames = nameConflict.groupValues[5].split(", ")
val type = when {
typeStr.equals("solving", ignoreCase = true) -> HunkType.SOLVING_NAME_CONFLICT
typeStr.equals("un-solving", ignoreCase = true) -> HunkType.UN_SOLVING_NAME_CONFLICT
else -> HunkType.UNKNOWN_NAME_CONFLICT_RESOLUTION
}
val (flags, flagsLastKnownLine) = this.readFlags(lastFound)
lastFound = flagsLastKnownLine
val (context, stoppedLine) = this.tryReadContext(lastFound)
lastFound = stoppedLine
hunks += NameConflictHunk(
hunkNumber = hunkNumber,
path = path,
type = type,
inode = inode,
deletedNames = deletedNames,
flags = flags,
meta = null,
context = context
)
}
val orderConflict = HUNK_ORDER_CONFLICT_PATTERN.matchEntire(this.substringUntil(lastFound, '\n'))
if (orderConflict != null) {
anyFound = true
lastFound += orderConflict.range.last
val hunkNumber = orderConflict.groupValues[1].toInt()
val typeStr = orderConflict.groupValues[2]
val path = orderConflict.groupValues[3]
val pos = orderConflict.groupValues.getOrNull(4)?.toIntOrNull()
val inode = orderConflict.groupValues[5].toDouble()
val type = when {
typeStr.equals("solving", ignoreCase = true) -> HunkType.SOLVING_ORDER_CONFLICT
typeStr.equals("un-solving", ignoreCase = true) -> HunkType.UN_SOLVING_ORDER_CONFLICT
else -> HunkType.UNKNOWN_ORDER_CONFLICT_RESOLUTION
}
val (flags, flagsLastKnownLine) = this.readFlags(lastFound)
lastFound = flagsLastKnownLine
val (context, stoppedLine) = this.tryReadContext(lastFound)
lastFound = stoppedLine
val (lines, linesLastKnownLine) = this.readLineChanges(lastFound)
lastFound = linesLastKnownLine
hunks += OrderConflictHunk(
hunkNumber = hunkNumber,
path = path,
index = pos,
type = type,
inode = inode,
lines = lines,
flags = flags,
meta = null,
context = context
)
}
val zombieLines = HUNK_ZOMBIE_PATTERN.matchEntire(this.substringUntil(lastFound, '\n'))
if (zombieLines != null) {
anyFound = true
lastFound += zombieLines.range.last
val hunkNumber = zombieLines.groupValues[1].toInt()
val path = zombieLines.groupValues[2]
val line = zombieLines.groupValues[3].toInt()
val inode = zombieLines.groupValues[4].toDouble()
val (flags, flagsLastKnownLine) = this.readFlags(lastFound)
lastFound = flagsLastKnownLine
val (context, stoppedLine) = this.tryReadContext(lastFound)
lastFound = stoppedLine
val (lines, linesLastKnownLine) = this.readLineChanges(lastFound)
lastFound = linesLastKnownLine
hunks += ZombieResurrectHunk(
hunkNumber = hunkNumber,
path = path,
line = line,
inode = inode,
lines = lines,
flags = flags,
meta = null,
context = context
)
}
if (!anyFound) {
lastFound = null
}
}
}
return PijulLogEntry(hash, message, timestamp.parseAsLocalDateTime(), authors, dependencies, hunks)
}
fun String.substringUntil(start: Int, char: Char, includeChar: Boolean = true) =
this.indexOf(char, start + 1).let {
if (it == -1) {
this.substring(start)
} else {
this.substring(start, if (includeChar) it + 1 else it)
}
}
fun String.tryReadContext(start: Int): Pair<Context?, Int> {
val end = this.indexOf('\n', start + 1)
val text = this.substring(start, end).trim()
val foundContext = HUNK_CONTEXT.find(text)
if (foundContext != null) {
val up = foundContext.groupValues[1].split(" ").map { it.toDouble() }
val newText = foundContext.groupValues.getOrNull(3)
val downText = foundContext.groupValues.getOrNull(4)
val new = if (newText != null && newText.startsWith(", new ")) {
val range = newText.substring(", new ".length)
val rangeDivisor = range.indexOf(':')
val firstPart = range.substring(0 until rangeDivisor).toInt()
val secondPart = range.substring(rangeDivisor + 1).toInt()
IntRange(firstPart, secondPart)
} else {
null
}
val down = if (downText != null && downText.startsWith(", down ")) {
downText.substring(", down ".length).split(" ").map { it.toDouble() }
} else {
emptyList()
}
return Context(up, new, down) to end
} else {
return null to start
}
}
fun String.readFlags(start: Int): Pair<List<Flag>, Int> {
val flags = mutableListOf<Flag>()
var lastKnownLine: Int = start
var lastLine: Int? = start
while (lastLine != null) {
if (this[lastLine] == '\n') {
lastLine += 1
lastKnownLine = lastLine
continue;
}
val foundFlag = FLAG_PATTERN.matchEntire(this.substringUntil(lastLine, '\n'))
if (foundFlag != null) {
lastKnownLine = lastLine + foundFlag.range.last
val flag = foundFlag.groupValues[1]
flags + Flag(flag)
lastLine = if (lastKnownLine.endsInNewLine(this)) {
null
} else {
lastKnownLine
}
} else {
lastLine = null
}
}
return flags to lastKnownLine
}
fun String.readLineChanges(start: Int): Pair<List<LineChange>, Int> {
val changes = mutableListOf<LineChange>()
var lastKnownLine: Int = start
var lastLine: Int? = start
while (lastLine != null) {
if (this[lastLine] == '\n') {
lastLine += 1;
lastKnownLine = lastLine
continue;
}
val foundChange = CHANGE_PATTERN.matchEntire(this.substringUntil(lastLine, '\n'))
if (foundChange != null) {
lastKnownLine = lastLine + foundChange.range.last
val symbol = foundChange.groupValues[1]
val data = foundChange.groupValues[2]
changes += LineChange(LineChangeType.fromSymbol(symbol), data)
lastLine = if (lastKnownLine.endsInNewLine(this)) {
null
} else {
lastKnownLine
}
} else {
// Single \ is used sometimes to represent an end of a change.
if (lastKnownLine < this.length && this[lastKnownLine] == '\\' && lastKnownLine + 1 < this.length) {
lastKnownLine += 1
}
lastLine = null
}
}
return changes to lastKnownLine
}
}
fun FileDelHunk.fileDelHunkAsSource() = "FileDelHunk(\n" +
"hunkNumber = $hunkNumber,\n" +
"type = ${this.type.hunkTypeAsSource()},\n" +
"path = \"$path\",\n" +
"inode = ${this.inode?.toString() ?: "null"},\n" +
"lines = ${lines.asSource()},\n" +
"meta = ${meta?.metaAsSource() ?: "null"},\n" +
"flags = ${flags.flagsAsSource()},\n" +
"context = ${context?.contextAsSource() ?: "null"},\n" +
")"
fun FileAddHunk.fileAddHunkAsSource() = "FileAddHunk(\n" +
"hunkNumber = $hunkNumber,\n" +
"type = ${this.type.hunkTypeAsSource()},\n" +
"path = \"$path\",\n" +
"rootPath = \"$rootPath\",\n" +
"lines = ${lines.asSource()},\n" +
"meta = ${meta?.metaAsSource() ?: "null"},\n" +
"flags = ${flags.flagsAsSource()},\n" +
"context = ${context?.contextAsSource() ?: "null"},\n" +
")"
fun FileUnDelHunk.fileUnDelHunkAsSource() = "FileUnDelHunk(\n" +
"hunkNumber = $hunkNumber,\n" +
"type = ${this.type.hunkTypeAsSource()},\n" +
"path = \"$path\",\n" +
"inode = ${this.inode?.toString() ?: "null"},\n" +
"lines = ${lines.asSource()},\n" +
"meta = ${meta?.metaAsSource() ?: "null"},\n" +
"flags = ${flags.flagsAsSource()},\n" +
"context = ${context?.contextAsSource() ?: "null"},\n" +
")"
fun MoveHunk.moveHunkAsSource() = "MoveHunk(\n" +
"hunkNumber = $hunkNumber,\n" +
"type = ${this.type.hunkTypeAsSource()},\n" +
"older = \"$older\",\n" +
"new = \"$new\",\n" +
"meta = ${meta?.metaAsSource() ?: "null"},\n" +
"flags = ${flags.flagsAsSource()},\n" +
"context = ${context?.contextAsSource() ?: "null"},\n" +
")"
fun EditHunk.editHunkAsSource() = "EditHunk(\n" +
"hunkNumber = $hunkNumber,\n" +
"type = ${this.type.hunkTypeAsSource()},\n" +
"path = \"$path\",\n" +
"startLine = $startLine,\n" +
"inode = ${inode?.toString() ?: "null"},\n" +
"lines = ${lines.asSource()},\n" +
"meta = ${meta?.metaAsSource() ?: "null"},\n" +
"flags = ${flags.flagsAsSource()},\n" +
"context = ${context?.contextAsSource() ?: "null"},\n" +
")"
fun ReplacementHunk.replacementHunkAsSource() = "ReplacementHunk(\n" +
"hunkNumber = $hunkNumber,\n" +
"type = ${this.type.hunkTypeAsSource()},\n" +
"path = \"$path\",\n" +
"startLine = $startLine,\n" +
"inode = ${inode?.toString() ?: "null"},\n" +
"lines = ${lines.asSource()},\n" +
"meta = ${meta?.metaAsSource() ?: "null"},\n" +
"flags = ${flags.flagsAsSource()},\n" +
"context = ${context?.contextAsSource() ?: "null"},\n" +
")"
fun NameConflictHunk.nameConflictHunkAsSource() = "NameConflictHunk(\n" +
"hunkNumber = $hunkNumber,\n" +
"type = ${this.type.hunkTypeAsSource()},\n" +
"path = \"$path\",\n" +
"inode = ${inode?.toString() ?: "null"},\n" +
"deletedNames = ${deletedNames.stringListAsSource()},\n" +
"meta = ${meta?.metaAsSource() ?: "null"},\n" +
"flags = ${flags.flagsAsSource()},\n" +
"context = ${context?.contextAsSource() ?: "null"},\n" +
")"
fun OrderConflictHunk.orderConflictHunkAsSource() = "OrderConflictHunk(\n" +
"hunkNumber = $hunkNumber,\n" +
"type = ${this.type.hunkTypeAsSource()},\n" +
"path = \"$path\",\n" +
"index = ${index?.toString() ?: "null"},\n" +
"inode = ${inode?.toString() ?: "null"},\n" +
"lines = ${lines.asSource()},\n" +
"meta = ${meta?.metaAsSource() ?: "null"},\n" +
"flags = ${flags.flagsAsSource()},\n" +
"context = ${context?.contextAsSource() ?: "null"},\n" +
")"
fun ZombieResurrectHunk.zombieResurrectHunkAsSource() = "ZombieResurrectHunk(\n" +
"hunkNumber = $hunkNumber,\n" +
"type = ${this.type.hunkTypeAsSource()},\n" +
"path = \"$path\",\n" +
"inode = ${inode?.toString() ?: "null"},\n" +
"lines = ${lines.asSource()},\n" +
"meta = ${meta?.metaAsSource() ?: "null"},\n" +
"flags = ${flags.flagsAsSource()},\n" +
"context = ${context?.contextAsSource() ?: "null"},\n" +
")"
fun HunkType.hunkTypeAsSource() = "HunkType.${this.name}"
fun List<LineChange>.asSource() =
if(this.isEmpty()) "emptyList()"
else "listOf(${this.joinToString(separator = ", ") { it.lineChangeAsSource() }})"
fun List<Flag>.flagsAsSource() =
if(this.isEmpty()) "emptyList()"
else "listOf(${this.joinToString(separator = ", ") { it.flagsAsSource() }})"
fun Meta.metaAsSource() = "Meta(\"${StringEscapeUtils.escapeJava(plain)}\")"
fun Flag.flagsAsSource() = "Flag(\"${StringEscapeUtils.escapeJava(plain)}\")"
fun List<String>.stringListAsSource() =
if (this.isEmpty()) "emptyList()"
else "listOf(\"${this.joinToString(separator = ", ") { StringEscapeUtils.escapeJava(it) }}\")"
fun Context.contextAsSource() = "Context(" +
"up = ${this.up.doublesAsSource()}, " +
"new = ${this.new?.intRangeAsSource() ?: "null"}, " +
"down = ${this.down.doublesAsSource()}" +
")"
fun LineChange.lineChangeAsSource() = "LineChange(" +
"type = LineChangeType.${type.name}, " +
"data = \"${StringEscapeUtils.escapeJava(data)}\"" +
")"
message = 'Initial CahngeProvider'
timestamp = '2021-03-29T01:50:38.537749028Z'
[[authors]]
name = 'Jonathan H. R. Lopes'
# Dependencies
[2] PGNTR2EPCZBOWI67LKY6AN5B3RGIEOQ6NTTXGODLESKDSPWV26KQC
[3]+FNNW5IEAXQ43WKB6QSQB7DFLG3Y3T5FYPXIUX7KQ2URR2GU3QLTAC
[4]+OPFG6CZ26PPTGTH7ULLRQGZGR3YEIEJOV5W2E3WN7PFRZS62CVLQC
[5]+GGYFPXND4VBCROZZXTKAP7Y4JOP2OOYQAFVLMUE7SLFM225EUSIAC
[*] NTRPUMVQHUIQZ6O72NJ72XFYTZWZOSDA6CSKMUCGKFVNE3KIDYYQC
# Hunks
1. File addition: "revision" in "src/main/kotlin/com/github/jonathanxd/dracon" +dx
up 3.107, new 0:10
2. File addition: "PijulRevisionNumber.kt" in "src/main/kotlin/com/github/jonathanxd/dracon/revision"
up 0.11, new 12:36
+ package com.github.jonathanxd.dracon.revision
+
+ import com.intellij.openapi.vcs.changes.patch.BlobIndexUtil
+ import com.intellij.openapi.vcs.history.ShortVcsRevisionNumber
+ import com.intellij.openapi.vcs.history.VcsRevisionNumber
+ import java.time.LocalDateTime
+ import java.time.ZonedDateTime
+
+ class PijulRevisionNumber(val hash: String, val timestamp: ZonedDateTime) : ShortVcsRevisionNumber {
+
+ val NOT_COMMITTED_HASH = BlobIndexUtil.NOT_COMMITTED_HASH
+
+ override fun compareTo(other: VcsRevisionNumber): Int {
+ if (this === other) return 0
+
+ if (other is PijulRevisionNumber) {
+ return this.timestamp.compareTo(other.timestamp)
+ }
+
+ return -1
+ }
+
+ override fun asString(): String = this.hash
+
+ override fun toShortString(): String = this.asString()
+ }
\
3. File addition: "provider" in "src/main/kotlin/com/github/jonathanxd/dracon" +dx
up 3.107, new 843:853
4. File addition: "PijulChangeProvider.kt" in "src/main/kotlin/com/github/jonathanxd/dracon/provider"
up 0.854, new 855:879
+ package com.github.jonathanxd.dracon.provider
+
+ import com.intellij.openapi.progress.ProgressIndicator
+ import com.intellij.openapi.project.Project
+ import com.intellij.openapi.vcs.VcsKey
+ import com.intellij.openapi.vcs.changes.*
+
+ class PijulChangeProvider(val project: Project, val key: VcsKey) : ChangeProvider {
+ override fun getChanges(
+ dirtyScope: VcsDirtyScope,
+ builder: ChangelistBuilder,
+ progress: ProgressIndicator,
+ addGate: ChangeListManagerGate
+ ) {
+ if (project.isDisposed) return
+ val dirtDirs = dirtyScope.recursivelyDirtyDirectories
+
+ println(dirtDirs)
+ }
+
+ override fun isModifiedDocumentTrackingRequired(): Boolean = true
+ }
\
5. Edit in src/main/kotlin/com/github/jonathanxd/dracon/pijul/Pijul.kt:13 3.4428
up 3.4473, new 1584:1698, down 3.4473
+ import com.github.jonathanxd.dracon.log.PijulLog
+ import com.github.jonathanxd.dracon.revision.PijulRevisionNumber
6. Edit in src/main/kotlin/com/github/jonathanxd/dracon/pijul/Pijul.kt:82 3.4428
up 3.6839, new 1699:1950, down 3.6839
+ @RequiresBackgroundThread
+ fun latestRevisionNumber(project: Project, root: VirtualFile): PijulOperationResult<PijulRevisionNumber>
+
+ @RequiresBackgroundThread
+ fun log(project: Project, root: VirtualFile): PijulOperationResult<PijulLog>
7. File addition: "PijulLog.kt" in "src/main/kotlin/com/github/jonathanxd/dracon/log"
up 3.6847, new 1951:1964
+ package com.github.jonathanxd.dracon.log
+
+ import java.time.LocalDateTime
+ import java.time.ZonedDateTime
+ import java.time.format.DateTimeFormatter
+ import java.time.format.ResolverStyle
+
+
+
+
+
+
+
+ class Author(val name: String?, val fullName: String?, val email: String?)
+ class PijulLog(val entries: List<PijulLogEntry>)
+
+ class PijulLogEntry(
+ val changeHash: String,
+ val message: String,
+ val date: ZonedDateTime,
+ val authors: List<Author>
+ )
+
+
+ val MESSAGE_PATTERN = Regex("message = '(.*)'\\n")
+ val TIME_PATTERN = Regex("timestamp = '(.*)'\\n")
+ val AUTHORS_SECTION_PATTERN = Regex("\\[\\[authors\\]\\]\n")
+ val AUTHOR_PATTERN = Regex("name = '(.*)'\\n")
+
+ fun String.parseChange(hash: String): PijulLogEntry {
+ val message = MESSAGE_PATTERN.find(this)?.groupValues?.get(1)!!
+ val timestamp = TIME_PATTERN.find(this)?.groupValues?.get(1)!!
+ val authorsSection = AUTHORS_SECTION_PATTERN.find(this)?.range?.endInclusive
+ val authors = mutableListOf<Author>()
+
+ if (authorsSection != null) {
+
+ var lastFound: Int? = authorsSection
+ while (lastFound != null) {
+ val foundAuthor = AUTHOR_PATTERN.find(this, lastFound)
+ val foundAuthorGroup = foundAuthor?.groupValues?.get(1)
+
+ if (foundAuthorGroup != null) {
+ authors += Author(foundAuthorGroup, null, null)
+ lastFound = if (foundAuthor.range.last + 1 < this.length && this[foundAuthor.range.last + 1] == '\n') {
+ null
+ } else {
+ foundAuthor.range.last
+ }
+ } else {
+ lastFound = null
+ }
+ }
+ }
+
+ return PijulLogEntry(hash, message, timestamp.parseAsLocalDateTime(), authors)
+ }
+
+ val RFC3339_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss[.SSSSSSSSS]XXX")
+ .withResolverStyle(ResolverStyle.LENIENT)
+
+ fun String.parseAsLocalDateTime(): ZonedDateTime {
+ return ZonedDateTime.parse(this, RFC3339_FORMATTER)
+ }
\
8. Edit in src/main/kotlin/com/github/jonathanxd/dracon/cmd/PijulCmd.kt:13 3.10972
up 3.11015, new 4000:4103, down 3.11015
+ import com.github.jonathanxd.dracon.log.PijulLog
+ import com.github.jonathanxd.dracon.log.PijulLogEntry
9. Edit in src/main/kotlin/com/github/jonathanxd/dracon/cmd/PijulCmd.kt:16 3.10972
up 3.11075, new 4104:4156, down 3.11075
+ import com.github.jonathanxd.dracon.log.parseChange
10. Edit in src/main/kotlin/com/github/jonathanxd/dracon/cmd/PijulCmd.kt:20 3.10972
up 4.2779, new 4157:4222, down 3.11119
+ import com.github.jonathanxd.dracon.revision.PijulRevisionNumber
11. Edit in src/main/kotlin/com/github/jonathanxd/dracon/cmd/PijulCmd.kt:106 3.10972
up 3.13610, new 4223:4490, down 3.13610
+
+ override fun log(project: Project, root: VirtualFile): PijulOperationResult<PijulLog> {
+ val rootPath = Paths.get(VcsUtil.getFilePath(root).path)
+
+ val logHashExecution = this.execPijul(project, rootPath, listOf("log", "--hash-only"), delay = 10L)
12. Edit in src/main/kotlin/com/github/jonathanxd/dracon/cmd/PijulCmd.kt:112 3.10972
up 3.13611, new 4491:5952, down 3.13611
+ val hashes = this.doExecutionWithMapper("log--hash-only", logHashExecution) {
+ it.lines()
+ }
+
+ if (hashes.statusCode !is SuccessStatusCode) {
+ return hashes as PijulOperationResult<PijulLog>
+ } else {
+ val entries = mutableListOf<PijulLogEntry>()
+ for (hash in hashes.result!!) {
+ val change = this.doExecutionWithMapper("change-$hash", this.execPijul(project, rootPath, listOf("change", hash), delay = 10L)) {
+ it.parseChange(hash)
+ }
+
+ if (change.statusCode !is SuccessStatusCode) {
+ return change as PijulOperationResult<PijulLog>
+ }
+ }
+
+ return PijulOperationResult(hashes.operation, hashes.statusCode, PijulLog(entries))
+ }
+ }
+
+ override fun latestRevisionNumber(project: Project, root: VirtualFile): PijulOperationResult<PijulRevisionNumber> {
+ /*val root = ProjectLevelVcsManager.getInstance(project).getVcsRootFor(file)
+ ?: return PijulOperationResult("file_status", SuccessStatusCode, FileStatus.UNKNOWN)*/
+ val rootPath = Paths.get(VcsUtil.getFilePath(root).path)
+
+ val log = this.log(project, root)
+
+ return PijulOperationResult(log.operation, log.statusCode,
+ log.result?.entries?.firstOrNull()?.let {
+ PijulRevisionNumber(it.changeHash, it.date)
+ }
+ )
+ }
+
13. Edit in src/main/kotlin/com/github/jonathanxd/dracon/PijulVcs.kt:14 3.24146
up 3.24239, new 5953:6018, down 3.24239
+ import com.github.jonathanxd.dracon.provider.PijulChangeProvider
14. Edit in src/main/kotlin/com/github/jonathanxd/dracon/PijulVcs.kt:20 3.24146
up 3.24475, new 6019:6074, down 3.24475
+ import com.intellij.openapi.vcs.changes.ChangeProvider
15. Edit in src/main/kotlin/com/github/jonathanxd/dracon/PijulVcs.kt:46 3.24146
up 3.25153, new 6075:6178, down 3.25153
+ override fun getChangeProvider(): ChangeProvider =
+ PijulChangeProvider(this.project, KEY)
+
16. File addition: "dada.toml" in "/"
up 1.0, new 6179:6190
17. Edit in build.gradle.kts:33 5.13712
up 5.14803, new 6193:6231, down 5.14803
+ maven(url = "https://jitpack.io")
18. Edit in build.gradle.kts:36 5.13712
up 5.14820, new 6232:6394, down 5.14820
+ implementation("org.tomlj:tomlj:1.0.0")
+ implementation("com.github.JonathanxD.JwIUtils:JwIUtils:4.17.0") {
+ exclude(group = "org.jetbrains")
+ }
19. Replacement in README.md:21 5.17946
B:BD 2.59651 -> 2.59651:59660/2
up 2.59651, new 6395:8239
- - [ ] ...
+ - [ ] ...
+
+
+ # FAQ
+
+ ## Why Dracon and not Pijul for IDEA (or something like that)?
+
+ Because Dracon is not an official plugin, and I want to make sure that it is clear to anyone who uses this plugin. Dracon is an individual project being developed by [Jonathan H. R. Lopes](https://github.com/JonathanxD) which does not have any relation with Pijul creator. Also, Dracon uses MIT license, allowing to anyone, and that includes the Pijul creator, to contribute and create your own version of Dracon, if wish to.
+
+ ## Why Dracon is developed against IntelliJ EAP instead of a stable releases?
+
+ Dracon is being developed based in actual [HG4Idea](https://github.com/JetBrains/intellij-community/tree/master/plugins/hg4idea) and [Git4Idea](https://github.com/JetBrains/intellij-community/tree/master/plugins/git4idea) plugins, as well based in current code of [IntelliJ IDEA Community](https://github.com/JetBrains/intellij-community/), and is planned to be released by the end of first semester of 2021, when release time comes, the IntelliJ EAP will be already released as stable, also I don't want to support older releases of IntelliJ IDEA because this involves basing on deprecated code, and this is my first IDEA plugin, so the more I focus the present than the past, more I deliver in terms of better and stable features.
+
+ ## Could I contribute to Dracon development?
+
+ I'm currently focused on learning how IDEA Plugin platform works and how DVCS is implemented in IDEA, so until Dracon is officially released, I will not be accepting contributions, but Dracon is licensed under MIT, you are free to create your own version of Dracon and developing it on your own.
+
+ ## When Dracon will be official released?
+
+ I will be periodically releasing Dragon experimental builds, but Dracon stable release is planned by the end of first semester of 2021.
\
testImplementation(platform("org.junit:junit-bom:5.7.1"))
testImplementation("org.junit.jupiter:junit-jupiter")
testImplementation("io.kotest:kotest-runner-junit5:4.4.3")
testImplementation("io.kotest:kotest-assertions-core:4.4.3")
testImplementation("io.kotest:kotest-property:4.4.3")
}
tasks.withType<Test> {
useJUnitPlatform()