From 27f216aa5db660e8a472949854f83a190309e3a2 Mon Sep 17 00:00:00 2001 From: Abdelilah El Aissaoui Date: Sat, 29 Jan 2022 20:33:08 +0100 Subject: [PATCH 1/2] Added basic images diff --- src/main/kotlin/app/git/DiffManager.kt | 11 +-- src/main/kotlin/app/git/RawFileManager.kt | 69 ++++++++++++-- src/main/kotlin/app/git/StatusManager.kt | 22 +++-- .../kotlin/app/git/diff/HunkDiffGenerator.kt | 19 +++- src/main/kotlin/app/ui/Diff.kt | 92 ++++++++++++++----- .../kotlin/app/viewmodels/DiffViewModel.kt | 11 ++- 6 files changed, 171 insertions(+), 53 deletions(-) diff --git a/src/main/kotlin/app/git/DiffManager.kt b/src/main/kotlin/app/git/DiffManager.kt index 59e7b72..da858ed 100644 --- a/src/main/kotlin/app/git/DiffManager.kt +++ b/src/main/kotlin/app/git/DiffManager.kt @@ -3,8 +3,8 @@ package app.git import app.di.HunkDiffGeneratorFactory import app.di.RawFileManagerFactory import app.extensions.fullData +import app.git.diff.DiffResult import app.git.diff.Hunk -import app.git.diff.HunkDiffGenerator import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext import org.eclipse.jgit.api.Git @@ -26,13 +26,12 @@ class DiffManager @Inject constructor( private val rawFileManagerFactory: RawFileManagerFactory, private val hunkDiffGeneratorFactory: HunkDiffGeneratorFactory, ) { - suspend fun diffFormat(git: Git, diffEntryType: DiffEntryType): List = withContext(Dispatchers.IO) { + suspend fun diffFormat(git: Git, diffEntryType: DiffEntryType): DiffResult = withContext(Dispatchers.IO) { val diffEntry = diffEntryType.diffEntry val byteArrayOutputStream = ByteArrayOutputStream() val repository = git.repository DiffFormatter(byteArrayOutputStream).use { formatter -> - formatter.setRepository(repository) val oldTree = DirCacheIterator(repository.readDirCache()) @@ -49,7 +48,7 @@ class DiffManager @Inject constructor( val rawFileManager = rawFileManagerFactory.create(repository) val hunkDiffGenerator = hunkDiffGeneratorFactory.create(repository, rawFileManager) - val hunks = mutableListOf() + var diffResult: DiffResult hunkDiffGenerator.use { if (diffEntryType is DiffEntryType.UnstagedDiff) { @@ -58,10 +57,10 @@ class DiffManager @Inject constructor( hunkDiffGenerator.scan(oldTree, newTree) } - hunks.addAll(hunkDiffGenerator.format(diffEntry)) + diffResult = hunkDiffGenerator.format(diffEntry) } - return@withContext hunks + return@withContext diffResult } suspend fun commitDiffEntries(git: Git, commit: RevCommit): List = withContext(Dispatchers.IO) { diff --git a/src/main/kotlin/app/git/RawFileManager.kt b/src/main/kotlin/app/git/RawFileManager.kt index 459f279..45dfe05 100644 --- a/src/main/kotlin/app/git/RawFileManager.kt +++ b/src/main/kotlin/app/git/RawFileManager.kt @@ -5,14 +5,18 @@ import dagger.assisted.AssistedInject import org.eclipse.jgit.diff.ContentSource import org.eclipse.jgit.diff.DiffEntry import org.eclipse.jgit.diff.RawText -import org.eclipse.jgit.lib.Constants -import org.eclipse.jgit.lib.FileMode -import org.eclipse.jgit.lib.ObjectReader -import org.eclipse.jgit.lib.Repository +import org.eclipse.jgit.errors.BinaryBlobException +import org.eclipse.jgit.lib.* import org.eclipse.jgit.storage.pack.PackConfig import org.eclipse.jgit.treewalk.AbstractTreeIterator import org.eclipse.jgit.treewalk.WorkingTreeIterator import org.eclipse.jgit.util.LfsFactory +import java.io.FileOutputStream +import java.nio.file.Path +import kotlin.io.path.absolutePathString +import kotlin.io.path.createTempDirectory +import kotlin.io.path.createTempFile + private const val DEFAULT_BINARY_FILE_THRESHOLD = PackConfig.DEFAULT_BIG_FILE_THRESHOLD @@ -22,6 +26,13 @@ class RawFileManager @AssistedInject constructor( private var reader: ObjectReader = repository.newObjectReader() private var source: ContentSource.Pair + private val imageFormatsSupported = listOf( + "png", + "jpg", + "jpeg", + "svg" + ) + init { val cs = ContentSource.create(reader) source = ContentSource.Pair(cs, cs) @@ -38,18 +49,60 @@ class RawFileManager @AssistedInject constructor( ContentSource.create(reader) } - fun getRawContent(side: DiffEntry.Side, entry: DiffEntry): RawText { - if (entry.getMode(side) === FileMode.MISSING) return RawText.EMPTY_TEXT - if (entry.getMode(side).objectType != Constants.OBJ_BLOB) return RawText.EMPTY_TEXT + fun getRawContent(side: DiffEntry.Side, entry: DiffEntry): EntryContent { + if (entry.getMode(side) === FileMode.MISSING) return EntryContent.Missing + if (entry.getMode(side).objectType != Constants.OBJ_BLOB) return EntryContent.InvalidObjectBlob val ldr = LfsFactory.getInstance().applySmudgeFilter( repository, source.open(side, entry), entry.diffAttribute ) - return RawText.load(ldr, DEFAULT_BINARY_FILE_THRESHOLD) + + return try { + EntryContent.Text(RawText.load(ldr, DEFAULT_BINARY_FILE_THRESHOLD)) + } catch (ex: BinaryBlobException) { + if(isImage(entry)) { + generateImageBinary(ldr, entry, side) + } else + EntryContent.Binary + } } + private fun generateImageBinary(ldr: ObjectLoader, entry: DiffEntry, side: DiffEntry.Side): EntryContent.ImageBinary { + println("Data's size is ${ldr.size}") + + val tempDir = createTempDirectory("gitnuro${repository.directory.absolutePath.replace("/", "_")}") + val tempFile = createTempFile(tempDir, prefix = "${entry.newPath}_${side.name}") + println("Temp file generated: ${tempFile.absolutePathString()}") + + val out = FileOutputStream(tempFile.toFile()) + out.use { + ldr.copyTo(out) + } + + return EntryContent.ImageBinary(tempFile) + } + + // todo check if it's an image checking the binary format, checking the extension is a temporary workaround + private fun isImage(entry: DiffEntry): Boolean { + val path = entry.newPath + val fileExtension = path.split(".").lastOrNull() ?: return false + + return imageFormatsSupported.contains(fileExtension) + } + +// fun isBinary() = RawText.isBinary() + override fun close() { reader.close() } +} + +sealed class EntryContent { + object Missing: EntryContent() + object InvalidObjectBlob: EntryContent() + data class Text(val rawText: RawText): EntryContent() + data class ImageBinary(val tempFilePath: Path): EntryContent() + object Binary: EntryContent() + object TooLargeEntry: EntryContent() } \ No newline at end of file diff --git a/src/main/kotlin/app/git/StatusManager.kt b/src/main/kotlin/app/git/StatusManager.kt index 9d4a9e4..76e6f35 100644 --- a/src/main/kotlin/app/git/StatusManager.kt +++ b/src/main/kotlin/app/git/StatusManager.kt @@ -12,8 +12,6 @@ import app.git.diff.Hunk import app.git.diff.LineType import app.theme.conflictFile import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.withContext import org.eclipse.jgit.api.Git import org.eclipse.jgit.diff.DiffEntry @@ -60,8 +58,12 @@ class StatusManager @Inject constructor( try { val rawFileManager = rawFileManagerFactory.create(git.repository) - val rawFile = rawFileManager.getRawContent(DiffEntry.Side.OLD, diffEntry) - val textLines = getTextLines(rawFile).toMutableList() + val entryContent = rawFileManager.getRawContent(DiffEntry.Side.OLD, diffEntry) + + if(entryContent !is EntryContent.Text) + return@withContext + + val textLines = getTextLines(entryContent.rawText).toMutableList() val hunkLines = hunk.lines.filter { it.lineType != LineType.CONTEXT } @@ -80,7 +82,7 @@ class StatusManager @Inject constructor( } } - val stagedFileText = textLines.joinToString(rawFile.lineDelimiter) + val stagedFileText = textLines.joinToString(entryContent.rawText.lineDelimiter) dirCacheEditor.add(HunkEdit(diffEntry.newPath, repository, ByteBuffer.wrap(stagedFileText.toByteArray()))) dirCacheEditor.commit() @@ -99,8 +101,12 @@ class StatusManager @Inject constructor( try { val rawFileManager = rawFileManagerFactory.create(git.repository) - val rawFile = rawFileManager.getRawContent(DiffEntry.Side.NEW, diffEntry) - val textLines = getTextLines(rawFile).toMutableList() + val entryContent = rawFileManager.getRawContent(DiffEntry.Side.NEW, diffEntry) + + if(entryContent !is EntryContent.Text) + return@withContext + + val textLines = getTextLines(entryContent.rawText).toMutableList() val hunkLines = hunk.lines.filter { it.lineType != LineType.CONTEXT } @@ -129,7 +135,7 @@ class StatusManager @Inject constructor( linesAdded++ } - val stagedFileText = textLines.joinToString(rawFile.lineDelimiter) + val stagedFileText = textLines.joinToString(entryContent.rawText.lineDelimiter) dirCacheEditor.add(HunkEdit(diffEntry.newPath, repository, ByteBuffer.wrap(stagedFileText.toByteArray()))) dirCacheEditor.commit() diff --git a/src/main/kotlin/app/git/diff/HunkDiffGenerator.kt b/src/main/kotlin/app/git/diff/HunkDiffGenerator.kt index cb4658a..545b93c 100644 --- a/src/main/kotlin/app/git/diff/HunkDiffGenerator.kt +++ b/src/main/kotlin/app/git/diff/HunkDiffGenerator.kt @@ -1,6 +1,7 @@ package app.git.diff import app.extensions.lineAt +import app.git.EntryContent import app.git.RawFileManager import dagger.assisted.Assisted import dagger.assisted.AssistedInject @@ -11,6 +12,7 @@ import org.eclipse.jgit.patch.FileHeader.PatchType import org.eclipse.jgit.treewalk.AbstractTreeIterator import java.io.ByteArrayOutputStream import java.io.IOException +import java.nio.file.Path import kotlin.math.max import kotlin.math.min @@ -37,11 +39,19 @@ class HunkDiffGenerator @AssistedInject constructor( diffFormatter.scan(oldTreeIterator, newTreeIterator) } - fun format(ent: DiffEntry): List { + fun format(ent: DiffEntry): DiffResult { val fileHeader = diffFormatter.toFileHeader(ent) + val rawOld = rawFileManager.getRawContent(DiffEntry.Side.OLD, ent) val rawNew = rawFileManager.getRawContent(DiffEntry.Side.NEW, ent) - return format(fileHeader, rawOld, rawNew) + + // todo won't work for new files + return if(rawOld is EntryContent.Text && rawNew is EntryContent.Text) + DiffResult.Text(format(fileHeader, rawOld.rawText, rawNew.rawText)) + else if(rawOld is EntryContent.ImageBinary && rawNew is EntryContent.ImageBinary) + DiffResult.Images(rawOld.tempFilePath, rawNew.tempFilePath) + else + DiffResult.Text(emptyList()) } /** @@ -142,4 +152,9 @@ class HunkDiffGenerator @AssistedInject constructor( private fun end(edit: Edit, a: Int, b: Int): Boolean { return edit.endA <= a && edit.endB <= b } +} + +sealed class DiffResult { + data class Text(val hunks: List): DiffResult() + data class Images(val oldTempFile: Path, val newTempsFile: Path): DiffResult() } \ No newline at end of file diff --git a/src/main/kotlin/app/ui/Diff.kt b/src/main/kotlin/app/ui/Diff.kt index 819e98a..6b6e22f 100644 --- a/src/main/kotlin/app/ui/Diff.kt +++ b/src/main/kotlin/app/ui/Diff.kt @@ -12,11 +12,13 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.ColorFilter +import androidx.compose.ui.res.loadImageBitmap import androidx.compose.ui.res.painterResource import androidx.compose.ui.text.font.FontFamily import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import app.git.DiffEntryType +import app.git.diff.DiffResult import app.git.diff.Hunk import app.git.diff.Line import app.git.diff.LineType @@ -25,6 +27,8 @@ import app.ui.components.ScrollableLazyColumn import app.ui.components.SecondaryButton import app.viewmodels.DiffViewModel import org.eclipse.jgit.diff.DiffEntry +import java.io.FileInputStream +import kotlin.io.path.absolutePathString import kotlin.math.max @Composable @@ -33,11 +37,11 @@ fun Diff( onCloseDiffView: () -> Unit, ) { val diffResultState = diffViewModel.diffResult.collectAsState() - val diffResult = diffResultState.value ?: return + val viewDiffResult = diffResultState.value ?: return - val diffEntryType = diffResult.diffEntryType + val diffEntryType = viewDiffResult.diffEntryType val diffEntry = diffEntryType.diffEntry - val hunks = diffResult.hunks + val diffResult = viewDiffResult.diffResult Column( modifier = Modifier @@ -46,35 +50,75 @@ fun Diff( .fillMaxSize() ) { DiffHeader(diffEntry, onCloseDiffView) + if (diffResult is DiffResult.Text) { + TextDiff(diffEntryType, diffViewModel, diffResult) + } else if (diffResult is DiffResult.Images) { + ImagesDiff(diffResult) + } + } +} - val scrollState by diffViewModel.lazyListState.collectAsState() - ScrollableLazyColumn( - modifier = Modifier - .fillMaxSize(), - state = scrollState - ) { - items(hunks) { hunk -> - HunkHeader( - hunk = hunk, - diffEntryType = diffEntryType, - diffViewModel = diffViewModel, - ) +@Composable +fun ImagesDiff(diffResult: DiffResult.Images) { + val oldImagePath = diffResult.oldTempFile + val newImagePath = diffResult.newTempsFile + Row( + modifier = Modifier + .fillMaxSize() + .background(Color.Red), + verticalAlignment = Alignment.CenterVertically + ) { + Image( + bitmap = loadImageBitmap(inputStream = FileInputStream(oldImagePath.absolutePathString())), + contentDescription = null, + modifier = Modifier.fillMaxWidth(0.5f) + .background(Color.Yellow), + ) + Spacer( + modifier = Modifier.fillMaxWidth(0.1f) + .background(Color.Green), + ) + Image( + bitmap = loadImageBitmap(inputStream = FileInputStream(newImagePath.absolutePathString())), + contentDescription = null, + modifier = Modifier.fillMaxWidth() + .background(Color.Blue), + ) + } +} - SelectionContainer { - Column { - val oldHighestLineNumber = hunk.lines.maxOf { it.displayOldLineNumber } - val newHighestLineNumber = hunk.lines.maxOf { it.displayNewLineNumber } - val highestLineNumber = max(oldHighestLineNumber, newHighestLineNumber) - val highestLineNumberLength = highestLineNumber.toString().count() +@Composable +fun TextDiff(diffEntryType: DiffEntryType, diffViewModel: DiffViewModel, diffResult: DiffResult.Text) { + val hunks = diffResult.hunks - hunk.lines.forEach { line -> - DiffLine(highestLineNumberLength, line) - } + val scrollState by diffViewModel.lazyListState.collectAsState() + ScrollableLazyColumn( + modifier = Modifier + .fillMaxSize(), + state = scrollState + ) { + items(hunks) { hunk -> + HunkHeader( + hunk = hunk, + diffEntryType = diffEntryType, + diffViewModel = diffViewModel, + ) + + SelectionContainer { + Column { + val oldHighestLineNumber = hunk.lines.maxOf { it.displayOldLineNumber } + val newHighestLineNumber = hunk.lines.maxOf { it.displayNewLineNumber } + val highestLineNumber = max(oldHighestLineNumber, newHighestLineNumber) + val highestLineNumberLength = highestLineNumber.toString().count() + + hunk.lines.forEach { line -> + DiffLine(highestLineNumberLength, line) } } } } } + } @Composable diff --git a/src/main/kotlin/app/viewmodels/DiffViewModel.kt b/src/main/kotlin/app/viewmodels/DiffViewModel.kt index 7ddff87..4399088 100644 --- a/src/main/kotlin/app/viewmodels/DiffViewModel.kt +++ b/src/main/kotlin/app/viewmodels/DiffViewModel.kt @@ -2,6 +2,7 @@ package app.viewmodels import androidx.compose.foundation.lazy.LazyListState import app.git.* +import app.git.diff.DiffResult import app.git.diff.Hunk import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow @@ -14,8 +15,8 @@ class DiffViewModel @Inject constructor( private val statusManager: StatusManager, ) { // TODO Maybe use a sealed class instead of a null to represent that a diff is not selected? - private val _diffResult = MutableStateFlow(null) - val diffResult: StateFlow = _diffResult + private val _diffResult = MutableStateFlow(null) + val diffResult: StateFlow = _diffResult val lazyListState = MutableStateFlow( LazyListState( @@ -44,10 +45,10 @@ class DiffViewModel @Inject constructor( //TODO: Just a workaround when trying to diff binary files try { val hunks = diffManager.diffFormat(git, diffEntryType) - _diffResult.value = DiffResult(diffEntryType, hunks) + _diffResult.value = ViewDiffResult(diffEntryType, hunks) } catch (ex: Exception) { ex.printStackTrace() - _diffResult.value = DiffResult(diffEntryType, emptyList()) + _diffResult.value = ViewDiffResult(diffEntryType, DiffResult.Text(emptyList())) } return@runOperation RefreshType.NONE @@ -66,4 +67,4 @@ class DiffViewModel @Inject constructor( } } -data class DiffResult(val diffEntryType: DiffEntryType, val hunks: List) \ No newline at end of file +data class ViewDiffResult(val diffEntryType: DiffEntryType, val diffResult: DiffResult) \ No newline at end of file From ef5bd9a25446df9c74ae8b4ba7d577d50fc6b042 Mon Sep 17 00:00:00 2001 From: Abdelilah El Aissaoui Date: Sun, 30 Jan 2022 02:27:41 +0100 Subject: [PATCH 2/2] Completed images & binaries diff --- src/main/kotlin/app/git/RawFileManager.kt | 8 +- .../kotlin/app/git/diff/HunkDiffGenerator.kt | 62 ++++++++-- src/main/kotlin/app/ui/Diff.kt | 110 ++++++++++++++---- src/main/resources/binary.svg | 3 + 4 files changed, 145 insertions(+), 38 deletions(-) create mode 100644 src/main/resources/binary.svg diff --git a/src/main/kotlin/app/git/RawFileManager.kt b/src/main/kotlin/app/git/RawFileManager.kt index 45dfe05..cb110aa 100644 --- a/src/main/kotlin/app/git/RawFileManager.kt +++ b/src/main/kotlin/app/git/RawFileManager.kt @@ -30,7 +30,6 @@ class RawFileManager @AssistedInject constructor( "png", "jpg", "jpeg", - "svg" ) init { @@ -72,7 +71,7 @@ class RawFileManager @AssistedInject constructor( println("Data's size is ${ldr.size}") val tempDir = createTempDirectory("gitnuro${repository.directory.absolutePath.replace("/", "_")}") - val tempFile = createTempFile(tempDir, prefix = "${entry.newPath}_${side.name}") + val tempFile = createTempFile(tempDir, prefix = "${entry.newPath.replace("/", "_")}_${side.name}") println("Temp file generated: ${tempFile.absolutePathString()}") val out = FileOutputStream(tempFile.toFile()) @@ -102,7 +101,8 @@ sealed class EntryContent { object Missing: EntryContent() object InvalidObjectBlob: EntryContent() data class Text(val rawText: RawText): EntryContent() - data class ImageBinary(val tempFilePath: Path): EntryContent() - object Binary: EntryContent() + sealed class BinaryContent() : EntryContent() + data class ImageBinary(val tempFilePath: Path): BinaryContent() + object Binary: BinaryContent() object TooLargeEntry: EntryContent() } \ No newline at end of file diff --git a/src/main/kotlin/app/git/diff/HunkDiffGenerator.kt b/src/main/kotlin/app/git/diff/HunkDiffGenerator.kt index 545b93c..d460dd5 100644 --- a/src/main/kotlin/app/git/diff/HunkDiffGenerator.kt +++ b/src/main/kotlin/app/git/diff/HunkDiffGenerator.kt @@ -12,11 +12,14 @@ import org.eclipse.jgit.patch.FileHeader.PatchType import org.eclipse.jgit.treewalk.AbstractTreeIterator import java.io.ByteArrayOutputStream import java.io.IOException -import java.nio.file.Path +import java.io.InvalidObjectException +import kotlin.contracts.ExperimentalContracts +import kotlin.contracts.contract import kotlin.math.max import kotlin.math.min private const val CONTEXT_LINES = 3 + /** * Generator of [Hunk] lists from [DiffEntry] */ @@ -39,19 +42,53 @@ class HunkDiffGenerator @AssistedInject constructor( diffFormatter.scan(oldTreeIterator, newTreeIterator) } - fun format(ent: DiffEntry): DiffResult { + fun format(ent: DiffEntry): DiffResult { val fileHeader = diffFormatter.toFileHeader(ent) val rawOld = rawFileManager.getRawContent(DiffEntry.Side.OLD, ent) val rawNew = rawFileManager.getRawContent(DiffEntry.Side.NEW, ent) - // todo won't work for new files - return if(rawOld is EntryContent.Text && rawNew is EntryContent.Text) - DiffResult.Text(format(fileHeader, rawOld.rawText, rawNew.rawText)) - else if(rawOld is EntryContent.ImageBinary && rawNew is EntryContent.ImageBinary) - DiffResult.Images(rawOld.tempFilePath, rawNew.tempFilePath) - else - DiffResult.Text(emptyList()) + if(rawOld == EntryContent.InvalidObjectBlob || rawNew == EntryContent.InvalidObjectBlob) + throw InvalidObjectException("Invalid object in diff format") + + var diffResult: DiffResult = DiffResult.Text(emptyList()) + + // If we can, generate text diff (if one of the files has never been a binary file) + val hasGeneratedTextDiff = canGenerateTextDiff(rawOld, rawNew) { oldRawText, newRawText -> + diffResult = DiffResult.Text(format(fileHeader, oldRawText, newRawText)) + } + + if (!hasGeneratedTextDiff) { + diffResult = DiffResult.NonText(rawOld, rawNew) + } + + return diffResult + } + + @OptIn(ExperimentalContracts::class) + private fun canGenerateTextDiff( + rawOld: EntryContent, + rawNew: EntryContent, + onText: (oldRawText: RawText, newRawText: RawText) -> Unit + ): Boolean { + + val rawOldText = when (rawOld) { + is EntryContent.Text -> rawOld.rawText + EntryContent.Missing -> RawText.EMPTY_TEXT + else -> null + } + + val newOldText = when (rawNew) { + is EntryContent.Text -> rawNew.rawText + EntryContent.Missing -> RawText.EMPTY_TEXT + else -> null + } + + return if(rawOldText != null && newOldText != null) { + onText(rawOldText, newOldText) + true + } else + false } /** @@ -155,6 +192,9 @@ class HunkDiffGenerator @AssistedInject constructor( } sealed class DiffResult { - data class Text(val hunks: List): DiffResult() - data class Images(val oldTempFile: Path, val newTempsFile: Path): DiffResult() + data class Text(val hunks: List) : DiffResult() + data class NonText( + val oldBinaryContent: EntryContent, + val newBinaryContent: EntryContent, + ) : DiffResult() } \ No newline at end of file diff --git a/src/main/kotlin/app/ui/Diff.kt b/src/main/kotlin/app/ui/Diff.kt index 6b6e22f..c6107ec 100644 --- a/src/main/kotlin/app/ui/Diff.kt +++ b/src/main/kotlin/app/ui/Diff.kt @@ -18,6 +18,7 @@ import androidx.compose.ui.text.font.FontFamily import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import app.git.DiffEntryType +import app.git.EntryContent import app.git.diff.DiffResult import app.git.diff.Hunk import app.git.diff.Line @@ -28,6 +29,7 @@ import app.ui.components.SecondaryButton import app.viewmodels.DiffViewModel import org.eclipse.jgit.diff.DiffEntry import java.io.FileInputStream +import java.nio.file.Path import kotlin.io.path.absolutePathString import kotlin.math.max @@ -52,41 +54,103 @@ fun Diff( DiffHeader(diffEntry, onCloseDiffView) if (diffResult is DiffResult.Text) { TextDiff(diffEntryType, diffViewModel, diffResult) - } else if (diffResult is DiffResult.Images) { - ImagesDiff(diffResult) + } else if (diffResult is DiffResult.NonText) { + NonTextDiff(diffResult) } } } @Composable -fun ImagesDiff(diffResult: DiffResult.Images) { - val oldImagePath = diffResult.oldTempFile - val newImagePath = diffResult.newTempsFile +fun NonTextDiff(diffResult: DiffResult.NonText) { + val oldBinaryContent = diffResult.oldBinaryContent + val newBinaryContent = diffResult.newBinaryContent + + val showOldAndNew = oldBinaryContent != EntryContent.Missing && newBinaryContent != EntryContent.Missing + Row( modifier = Modifier - .fillMaxSize() - .background(Color.Red), + .fillMaxSize(), verticalAlignment = Alignment.CenterVertically ) { - Image( - bitmap = loadImageBitmap(inputStream = FileInputStream(oldImagePath.absolutePathString())), - contentDescription = null, - modifier = Modifier.fillMaxWidth(0.5f) - .background(Color.Yellow), - ) - Spacer( - modifier = Modifier.fillMaxWidth(0.1f) - .background(Color.Green), - ) - Image( - bitmap = loadImageBitmap(inputStream = FileInputStream(newImagePath.absolutePathString())), - contentDescription = null, - modifier = Modifier.fillMaxWidth() - .background(Color.Blue), - ) + + if (showOldAndNew) { + Column( + modifier = Modifier.weight(0.5f) + .padding(start = 24.dp, end = 8.dp, top = 24.dp, bottom = 24.dp), + horizontalAlignment = Alignment.CenterHorizontally, + ) { + SideTitle("Old") + SideDiff(oldBinaryContent) + } + Column( + modifier = Modifier.weight(0.5f) + .padding(start = 8.dp, end = 24.dp, top = 24.dp, bottom = 24.dp), + horizontalAlignment = Alignment.CenterHorizontally, + ) { + SideTitle("New") + SideDiff(newBinaryContent) + } + } else if(oldBinaryContent != EntryContent.Missing) { + Box( + modifier = Modifier.fillMaxSize() + .padding(all = 24.dp), + ) { + SideDiff(oldBinaryContent) + } + } else if(newBinaryContent != EntryContent.Missing) { + Column( + modifier = Modifier.fillMaxSize() + .padding(all = 24.dp), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.Center, + ) { + SideTitle("Binary file") + Spacer(modifier = Modifier.height(24.dp)) + SideDiff(newBinaryContent) + } + } } } +@Composable +fun SideTitle(text: String) { + Text( + text = text, + fontSize = 20.sp, + color = MaterialTheme.colors.primaryTextColor, + ) +} + +@Composable +fun SideDiff(entryContent: EntryContent) { + when (entryContent) { + EntryContent.Binary -> BinaryDiff() + is EntryContent.ImageBinary -> ImageDiff(entryContent.tempFilePath) + else -> {} +// is EntryContent.Text -> //TODO maybe have a text view if the file was a binary before? +// TODO Show some info about this EntryContent.TooLargeEntry -> TODO() + } +} + +@Composable +fun ImageDiff(tempImagePath: Path) { + Image( + bitmap = loadImageBitmap(inputStream = FileInputStream(tempImagePath.absolutePathString())), + contentDescription = null, + modifier = Modifier.fillMaxSize() + ) +} + +@Composable +fun BinaryDiff() { + Image( + painter = painterResource("binary.svg"), + contentDescription = null, + modifier = Modifier.width(400.dp), + colorFilter = ColorFilter.tint(MaterialTheme.colors.primary) + ) +} + @Composable fun TextDiff(diffEntryType: DiffEntryType, diffViewModel: DiffViewModel, diffResult: DiffResult.Text) { val hunks = diffResult.hunks diff --git a/src/main/resources/binary.svg b/src/main/resources/binary.svg new file mode 100644 index 0000000..669d4ff --- /dev/null +++ b/src/main/resources/binary.svg @@ -0,0 +1,3 @@ + + +