First record support, YEAAAH, RECOOORD

[?]
Mar 31, 2021, 5:05 AM
Q7FXTHVUPVAFMNY277C3NFJO3VXLZU5G6C6UYSD5QPURHSG3A7OQC

Dependencies

  • [2] MTPTFTHG Initial plugin 2
  • [3] FRFFQV7V Basic show history support.
  • [4] GGYFPXND Initial plugin
  • [5] OPFG6CZ2 File status tracking supported.
  • [6] FNNW5IEA Added more plugin files to Pijul
  • [7] 6CR2EFUN First ChangeProvider implementation!!! Wheehooo
  • [*] QXUEMZ3B Initial CahngeProvider

Change contents

  • edit in src/main/resources/messages/DraconBundle.properties at line 14
    [4.165]
    [4.165]
  • replacement in src/main/resources/messages/DraconBundle.properties at line 28
    [2.286][2.286:314]()
    group.Pijul.Menu.text=_Pijul
    [2.286]
    group.Pijul.Menu.text=_Pijul
    record.action.name=Record
    record.author=&Author:
  • edit in src/main/resources/META-INF/plugin.xml at line 35
    [2.494]
    [2.494]
    <projectService serviceImplementation="com.github.jonathanxd.dracon.config.PijulSettings" configurationSchemaKey="versionControl.pijul"/>
  • edit in src/main/kotlin/com/github/jonathanxd/dracon/revision/RevisionContentResolver.kt at line 44
    [3.2250]
    [3.2250]
    @OptIn(ExperimentalPathApi::class)
    fun loadStateInRevision(revisionHash: String,
    project: Project,
    root: Path,
    filePath: Path): String {
    val tmpTarget = Paths.get(project.baseDir.path, ".idea", "dracon_diffs", revisionHash)
    if (Files.exists(tmpTarget)) {
    Files.walk(tmpTarget).use { walk ->
    walk.sorted(Comparator.reverseOrder())
    .map(Path::toFile)
    .forEach(File::delete)
    }
    }
    Files.createDirectories(tmpTarget)
    copyFolder(root, tmpTarget, StandardCopyOption.COPY_ATTRIBUTES, StandardCopyOption.REPLACE_EXISTING)
    val revisions = pijul(project).latestRevisionNumber(project, tmpTarget).result
    if (revisions != null && revisions.hash == revisionHash) {
    val rollbackOp = pijul(project).reset(project, tmpTarget)
    } else {
    val resetOp = pijul(project).reset(project, tmpTarget)
    val rollbackOp = pijul(project).rollbackTo(revisionHash, project, tmpTarget)
    }
  • edit in src/main/kotlin/com/github/jonathanxd/dracon/revision/RevisionContentResolver.kt at line 71
    [3.2251]
    [3.2251]
    return Files.readString(tmpTarget.resolve(filePath))
    }
  • edit in src/main/kotlin/com/github/jonathanxd/dracon/revision/PijulVcsFileRevision.kt at line 9
    [3.3431]
    [3.3431]
    import java.nio.file.Paths
  • edit in src/main/kotlin/com/github/jonathanxd/dracon/revision/PijulVcsFileRevision.kt at line 11
    [3.3450]
    [3.3450]
    import kotlin.io.path.ExperimentalPathApi
    import kotlin.io.path.relativeTo
  • replacement in src/main/kotlin/com/github/jonathanxd/dracon/revision/PijulVcsFileRevision.kt at line 16
    [3.3546][3.3546:3597]()
    val filePath: FilePath,
    [3.3546]
    [3.3597]
    val filePath: Path,
  • edit in src/main/kotlin/com/github/jonathanxd/dracon/revision/PijulVcsFileRevision.kt at line 21
    [3.3825]
    [3.3825]
    @OptIn(ExperimentalPathApi::class)
    constructor(project: Project,
    vcsRoot: Path,
    filePath: FilePath,
    revision: PijulRevisionNumber,
    author_: String?,
    message: String?,
    branch: String?): this(project, vcsRoot, Paths.get(filePath.path).relativeTo(vcsRoot), revision, author_, message, branch)
  • file addition: PijulDiffProvider.kt (----------)
    [9.854]
    package com.github.jonathanxd.dracon.provider
    import com.github.jonathanxd.dracon.content.PijulContentRevision
    import com.github.jonathanxd.dracon.pijul.pijul
    import com.github.jonathanxd.dracon.revision.PijulRevisionNumber
    import com.intellij.openapi.components.Service
    import com.intellij.openapi.project.Project
    import com.intellij.openapi.vcs.FilePath
    import com.intellij.openapi.vcs.FileStatus
    import com.intellij.openapi.vcs.ProjectLevelVcsManager
    import com.intellij.openapi.vcs.changes.ContentRevision
    import com.intellij.openapi.vcs.diff.DiffProvider
    import com.intellij.openapi.vcs.diff.ItemLatestState
    import com.intellij.openapi.vcs.history.VcsRevisionNumber
    import com.intellij.openapi.vfs.VirtualFile
    import java.nio.file.Paths
    import kotlin.io.path.ExperimentalPathApi
    import kotlin.io.path.relativeTo
    @Service
    class PijulDiffProvider(val project: Project) : DiffProvider {
    override fun getCurrentRevision(file: VirtualFile): VcsRevisionNumber? {
    val root = ProjectLevelVcsManager.getInstance(this.project).getVcsRootFor(file)?.toNioPath() ?: return null
    return pijul(project).latestRevisionNumberForPath(this.project, root, file.toNioPath()).result
    }
    @OptIn(ExperimentalPathApi::class)
    fun getCurrentRevision(path: FilePath): VcsRevisionNumber? {
    val root = ProjectLevelVcsManager.getInstance(this.project).getVcsRootFor(path)?.toNioPath() ?: return null
    val filePath = path.ioFile.toPath().relativeTo(root)
    return pijul(project).latestRevisionNumberForPath(this.project, root, filePath).result
    }
    override fun getLastRevision(virtualFile: VirtualFile): ItemLatestState? {
    val current = this.getCurrentRevision(virtualFile) ?: return null
    return ItemLatestState(current, virtualFile.exists(), true)
    }
    override fun getLastRevision(filePath: FilePath): ItemLatestState? {
    val current = this.getCurrentRevision(filePath) ?: return null
    return ItemLatestState(current, filePath.ioFile.exists(), true)
    }
    override fun createFileContent(revisionNumber: VcsRevisionNumber, selectedFile: VirtualFile): ContentRevision? {
    val root = ProjectLevelVcsManager.getInstance(this.project).getVcsRootFor(selectedFile)?.toNioPath() ?: return null
    return PijulContentRevision(
    root,
    selectedFile.toNioPath(),
    revisionNumber as? PijulRevisionNumber ?: return null,
    this.project
    )
    }
    override fun getLatestCommittedRevision(vcsRoot: VirtualFile?): VcsRevisionNumber? {
    return null
    }
    }
  • edit in src/main/kotlin/com/github/jonathanxd/dracon/pijul/Pijul.kt at line 26
    [4.4986]
    [4.4986]
    import com.intellij.vcs.log.VcsUser
  • edit in src/main/kotlin/com/github/jonathanxd/dracon/pijul/Pijul.kt at line 87
    [3.13172]
    [4.4515]
    @RequiresBackgroundThread
    fun latestRevisionNumberForPath(project: Project, root: Path, filePath: Path): PijulOperationResult<PijulRevisionNumber>
  • edit in src/main/kotlin/com/github/jonathanxd/dracon/pijul/Pijul.kt at line 97
    [9.1839]
    [4.4747]
    @RequiresBackgroundThread
    fun record(project: Project, root: Path, files: List<Path>, author: VcsUser?, message: String): PijulOperationResult<Boolean>
  • edit in src/main/kotlin/com/github/jonathanxd/dracon/i18n/DraconBundle.kt at line 58
    [4.9820]
    [4.9820]
    object Record {
    val name
    get() = message("record.action.name")
    }
  • edit in src/main/kotlin/com/github/jonathanxd/dracon/content/PijulContentRevision.kt at line 9
    [4.5326]
    [4.5326]
    import com.intellij.openapi.vcs.LocalFilePath
  • replacement in src/main/kotlin/com/github/jonathanxd/dracon/content/PijulContentRevision.kt at line 24
    [4.5750][4.5750:5778]()
    val filePath: FilePath,
    [4.5750]
    [4.5778]
    val filePath: Path,
  • edit in src/main/kotlin/com/github/jonathanxd/dracon/content/PijulContentRevision.kt at line 29
    [4.5864]
    [4.5864]
    @OptIn(ExperimentalPathApi::class)
    constructor(root: Path,
    filePath: FilePath,
    revision: PijulRevisionNumber,
    project: Project): this(root, Paths.get(filePath.path).relativeTo(root), revision, project)
  • replacement in src/main/kotlin/com/github/jonathanxd/dracon/content/PijulContentRevision.kt at line 39
    [4.5987][4.5987:6009]()
    this.filePath
    [4.5987]
    [4.6009]
    LocalFilePath(this.filePath, Files.isDirectory(this.filePath))
  • file addition: config (d--r------)
    [4.107]
  • file addition: PijulSettings.kt (----------)
    [0.5365]
    package com.github.jonathanxd.dracon.config
    import com.intellij.dvcs.branch.DvcsCompareSettings
    import com.intellij.dvcs.branch.DvcsSyncSettings
    import com.intellij.openapi.components.*
    import com.intellij.util.xmlb.annotations.OptionTag
    import com.intellij.vcs.log.VcsUser
    @State(name = "Pijul.Settings", storages = [Storage(StoragePathMacros.WORKSPACE_FILE)])
    class PijulSettings : SimplePersistentStateComponent<PijulOptions>(PijulOptions()), DvcsSyncSettings, DvcsCompareSettings {
    override fun getSyncSetting(): DvcsSyncSettings.Value =
    this.state.rootSync
    fun getAuthor(): String? {
    return this.state.author
    }
    fun saveAuthor(author: VcsUser) { // Pijul still does not support specifying emails?
    this.state.author = author.name
    }
    override fun setSyncSetting(syncSetting: DvcsSyncSettings.Value) {
    this.state.rootSync = syncSetting
    }
    override fun shouldSwapSidesInCompareBranches(): Boolean =
    this.state.isSwapSidesInCompareBranches
    override fun setSwapSidesInCompareBranches(value: Boolean) {
    this.state.isSwapSidesInCompareBranches = value
    }
    }
    class PijulOptions : BaseState() {
    @get:OptionTag("PATH_TO_PIJUL")
    var pathToGit by string()
    @get:OptionTag("AUTHOR")
    var author by string()
    @get:OptionTag("ROOT_SYNC")
    var rootSync by enum(DvcsSyncSettings.Value.NOT_DECIDED)
    @get:OptionTag("SWAP_SIDES_IN_COMPARE_BRANCHES")
    var isSwapSidesInCompareBranches by property(false)
    }
  • edit in src/main/kotlin/com/github/jonathanxd/dracon/cmd/PijulCmd.kt at line 31
    [4.11640]
    [4.11640]
    import com.intellij.vcs.log.VcsUser
  • replacement in src/main/kotlin/com/github/jonathanxd/dracon/cmd/PijulCmd.kt at line 95
    [4.3198][3.15577:15689]()
    val execution = this.createExecPijulOperation(project, rootPath, listOf("diff", "--json"), delay = 10L)
    [4.3198]
    [4.13603]
    val execution = this.createPainlessExecPijulOperation(project, rootPath, listOf("diff", "--json"))
  • edit in src/main/kotlin/com/github/jonathanxd/dracon/cmd/PijulCmd.kt at line 169
    [4.3734]
    [4.10745]
    }
    }
    override fun record(
    project: Project,
    root: Path,
    files: List<Path>,
    author: VcsUser?,
    message: String
    ): PijulOperationResult<Boolean> {
    val arguments = mutableListOf<String>("record")
    if (author != null) {
    arguments.add("--author")
    arguments.add(author.name)
    }
    arguments.add("--message")
    arguments.add(message)
    if (files.isNotEmpty()) {
    arguments.add("--")
    arguments.addAll(files.map { it.toString() })
    } else {
    return PijulOperationResult("record", NonZeroExitStatusCode(-1, "Empty list of paths to record"), false)
  • edit in src/main/kotlin/com/github/jonathanxd/dracon/cmd/PijulCmd.kt at line 195
    [4.10755]
    [4.10755]
    return this.doExecutionWithMapper("record", this.createPainlessExecPijulOperation(this.project, root, arguments)) {
    true
    }
  • edit in src/main/kotlin/com/github/jonathanxd/dracon/cmd/PijulCmd.kt at line 346
    [3.19677]
    [3.19677]
    override fun latestRevisionNumberForPath(
    project: Project,
    root: Path,
    filePath: Path
    ): PijulOperationResult<PijulRevisionNumber> {
    val log = this.log(project, root)
    return PijulOperationResult(log.operation, log.statusCode,
    log.result?.entries?.firstOrNull {
    it.hunks.filterIsInstance<HunkWithPath>().firstOrNull {
    filePath.toString().equals(it.resolvedPath, ignoreCase = true)
    } != null
    }?.let {
    PijulRevisionNumber(it.changeHash, it.date)
    }
    )
    }
  • file addition: checkin (d--r------)
    [4.107]
  • file addition: PijulCheckingEnvironment.kt (----------)
    [0.8526]
    package com.github.jonathanxd.dracon.checkin
    import com.github.jonathanxd.dracon.config.PijulSettings
    import com.github.jonathanxd.dracon.i18n.DraconBundle
    import com.github.jonathanxd.dracon.pijul.SuccessStatusCode
    import com.github.jonathanxd.dracon.pijul.pijul
    import com.intellij.openapi.Disposable
    import com.intellij.openapi.components.Service
    import com.intellij.openapi.components.service
    import com.intellij.openapi.project.Project
    import com.intellij.openapi.ui.popup.Balloon
    import com.intellij.openapi.util.Key
    import com.intellij.openapi.vcs.CheckinProjectPanel
    import com.intellij.openapi.vcs.FilePath
    import com.intellij.openapi.vcs.ProjectLevelVcsManager
    import com.intellij.openapi.vcs.VcsException
    import com.intellij.openapi.vcs.changes.*
    import com.intellij.openapi.vcs.checkin.CheckinChangeListSpecificComponent
    import com.intellij.openapi.vcs.checkin.CheckinEnvironment
    import com.intellij.openapi.vcs.ui.RefreshableOnComponent
    import com.intellij.openapi.vfs.VirtualFile
    import com.intellij.ui.components.JBLabel
    import com.intellij.util.ui.GridBag
    import com.intellij.util.ui.JBUI
    import com.intellij.vcs.commit.*
    import com.intellij.vcs.log.VcsUser
    import com.intellij.vcs.log.VcsUserEditor
    import java.awt.GridBagConstraints
    import java.awt.GridBagLayout
    import java.awt.event.*
    import java.util.*
    import javax.swing.JComponent
    import javax.swing.JPanel
    import kotlin.io.path.ExperimentalPathApi
    import kotlin.io.path.relativeTo
    val COMMIT_AUTHOR_KEY = Key.create<VcsUser>("Pijul.Commit.Author")
    val COMMIT_AUTHOR_DATE_KEY = Key.create<Date>("Pijul.Commit.AuthorDate")
    internal var CommitContext.commitAuthor: VcsUser? by commitProperty(COMMIT_AUTHOR_KEY, null)
    internal var CommitContext.commitAuthorDate: Date? by commitProperty(COMMIT_AUTHOR_DATE_KEY, null)
    @Service
    class PijulCheckingEnvironment(val project: Project) : CheckinEnvironment {
    override fun createCommitOptions(
    commitPanel: CheckinProjectPanel,
    commitContext: CommitContext
    ): RefreshableOnComponent? {
    return PijulCheckinOptions(commitPanel, commitContext)
    }
    override fun getHelpId(): String? = null
    override fun getCheckinOperationName(): String =
    DraconBundle.Record.name
    override fun scheduleMissingFileForDeletion(files: MutableList<out FilePath>): MutableList<VcsException> {
    return mutableListOf()
    }
    override fun scheduleUnversionedFilesForAddition(files: MutableList<out VirtualFile>): MutableList<VcsException> {
    return mutableListOf()
    }
    override fun isRefreshAfterCommitNeeded(): Boolean = true
    @OptIn(ExperimentalPathApi::class)
    override fun commit(
    changes: MutableList<out Change>,
    commitMessage: String,
    commitContext: CommitContext,
    feedback: MutableSet<in String>
    ): MutableList<VcsException>? {
    val vcsManager = ProjectLevelVcsManager.getInstance(this.project)
    val root = vcsManager.allVcsRoots.first().path.toNioPath()
    val paths = changes.map {
    val root = vcsManager.getVcsRootFor(it.afterRevision!!.file)!!.toNioPath()
    val relative = it.afterRevision!!.file.ioFile.toPath().relativeTo(root)
    relative
    }
    println(paths)
    val author = commitContext.commitAuthor
    val record = pijul(project).record(project, root, paths, author, commitMessage)
    return if (record.statusCode !is SuccessStatusCode) {
    mutableListOf(
    VcsException("Operation ${record.operation}, failed with statusCode: ${record.statusCode}")
    )
    } else {
    super.commit(changes, commitMessage, commitContext, feedback)
    }
    }
    class PijulCheckinOptions(val commitPanel: CheckinProjectPanel, val commitContext: CommitContext) : RefreshableOnComponent, Disposable {
    val optionsUi = PijulOptionsUi(commitPanel, commitContext)
    override fun refresh() {
    optionsUi.refresh()
    }
    override fun saveState() {
    optionsUi.saveState()
    }
    override fun restoreState() {
    optionsUi.restoreState()
    }
    override fun getComponent(): JComponent {
    return optionsUi.component
    }
    override fun dispose() {
    optionsUi.dispose()
    }
    }
    class PijulOptionsUi(private val commitPanel: CheckinProjectPanel,
    private val commitContext: CommitContext) : RefreshableOnComponent,
    CheckinChangeListSpecificComponent,
    CommitAuthorListener,
    Disposable {
    private val CheckinProjectPanel.commitAuthorTracker: CommitAuthorTracker? get() = commitWorkflowHandler as? CommitAuthorTracker
    private val project get() = commitPanel.project
    private val settings = project.service<PijulSettings>()
    private var authorDate: Date? = null
    private val panel = JPanel(GridBagLayout())
    private val authorField = VcsUserEditor(project, getKnownCommitAuthors())
    init {
    authorField.addFocusListener(object : FocusAdapter() {
    override fun focusLost(e: FocusEvent) {
    updateCurrentCommitAuthor()
    }
    })
    buildLayout()
    }
    private fun buildLayout() = panel.apply {
    val gb = GridBag().setDefaultAnchor(GridBagConstraints.WEST).setDefaultInsets(JBUI.insets(2))
    val authorLabel = JBLabel(DraconBundle.message("record.author")).apply { labelFor = authorField }
    add(authorLabel, gb.nextLine().next())
    add(authorField, gb.next().fillCellHorizontally().weightx(1.0))
    }
    override fun dispose() = Unit
    override fun getComponent(): JComponent = panel
    override fun restoreState() {
    if (commitPanel.isNonModalCommit) {
    commitPanel.commitAuthorTracker?.addCommitAuthorListener(this, this)
    }
    refresh()
    }
    override fun refresh() {
    refresh(null)
    commitAuthorChanged()
    }
    override fun saveState() {
    val author = getAuthor()
    commitContext.apply {
    commitAuthor = author
    commitAuthorDate = authorDate
    }
    settings.apply {
    author?.let { saveAuthor(it) }
    }
    }
    override fun onChangeListSelected(list: LocalChangeList) {
    refresh(list)
    panel.revalidate()
    panel.repaint()
    }
    private fun refresh(changeList: LocalChangeList?) {
    setAuthor(changeList?.author)
    authorDate = changeList?.authorDate
    }
    fun getAuthor(): VcsUser? = authorField.user
    private fun setAuthor(author: VcsUser?) {
    val isAuthorNull = author == null
    authorField.user = author.takeUnless { isAuthorNull }
    }
    private fun updateCurrentCommitAuthor() {
    commitPanel.commitAuthorTracker?.commitAuthor = getAuthor()
    }
    override fun commitAuthorChanged() {
    val newAuthor = commitPanel.commitAuthorTracker?.commitAuthor
    if (getAuthor() != newAuthor) setAuthor(newAuthor)
    }
    private fun getKnownCommitAuthors(): List<String> =
    (VcsUserEditor.getAllUsers(project) + settings.getAuthor()?.let { listOf(it) }.orEmpty()).distinct().sorted()
    }
    }
  • edit in src/main/kotlin/com/github/jonathanxd/dracon/PijulVcs.kt at line 13
    [4.24185]
    [4.24185]
    import com.github.jonathanxd.dracon.checkin.PijulCheckingEnvironment
  • edit in src/main/kotlin/com/github/jonathanxd/dracon/PijulVcs.kt at line 16
    [9.6018]
    [3.23467]
    import com.github.jonathanxd.dracon.provider.PijulDiffProvider
  • edit in src/main/kotlin/com/github/jonathanxd/dracon/PijulVcs.kt at line 25
    [9.6074]
    [3.23581]
    import com.intellij.openapi.vcs.checkin.CheckinEnvironment
    import com.intellij.openapi.vcs.diff.DiffProvider
  • edit in src/main/kotlin/com/github/jonathanxd/dracon/PijulVcs.kt at line 28
    [3.23640]
    [4.24475]
    import com.intellij.openapi.vcs.history.VcsRevisionNumber
  • edit in src/main/kotlin/com/github/jonathanxd/dracon/PijulVcs.kt at line 63
    [3.23854]
    [4.25153]
    override fun getDiffProvider(): DiffProvider =
    project.service<PijulDiffProvider>()
  • edit in src/main/kotlin/com/github/jonathanxd/dracon/PijulVcs.kt at line 69
    [4.25245]
    [4.25245]
    override fun getCheckinEnvironment(): CheckinEnvironment =
    project.service<PijulCheckingEnvironment>()