Clicking on a submodule in (un)commited changes shows an informative screen

This commit is contained in:
Abdelilah El Aissaoui 2023-09-14 01:05:06 +02:00
parent f8a6884098
commit 5f2180f1a3
No known key found for this signature in database
GPG Key ID: 7587FC860F594869
7 changed files with 130 additions and 63 deletions

View File

@ -43,6 +43,7 @@ class RawFileManager @Inject constructor(
newTreeIterator: AbstractTreeIterator?,
): EntryContent {
if (entry.getMode(side) === FileMode.MISSING) return EntryContent.Missing
if (entry.getMode(side).objectType == Constants.OBJ_COMMIT) return EntryContent.Submodule
if (entry.getMode(side).objectType != Constants.OBJ_BLOB) return EntryContent.InvalidObjectBlob
val reader: ObjectReader = repository.newObjectReader()
@ -111,6 +112,7 @@ sealed class EntryContent {
object Missing : EntryContent()
object InvalidObjectBlob : EntryContent()
data class Text(val rawText: RawText) : EntryContent()
object Submodule : EntryContent()
sealed class BinaryContent : EntryContent()
data class ImageBinary(val imagePath: String, val contentType: String) : BinaryContent()
object Binary : BinaryContent()

View File

@ -2,6 +2,7 @@ package com.jetpackduba.gitnuro.git.diff
import com.jetpackduba.gitnuro.git.EntryContent
import org.eclipse.jgit.diff.DiffEntry
import org.eclipse.jgit.submodule.SubmoduleStatus
sealed class DiffResult(
val diffEntry: DiffEntry,
@ -21,4 +22,9 @@ sealed class DiffResult(
val oldBinaryContent: EntryContent,
val newBinaryContent: EntryContent,
) : DiffResult(diffEntry)
class Submodule(
diffEntry: DiffEntry,
val submoduleStatus: SubmoduleStatus?,
) : DiffResult(diffEntry)
}

View File

@ -1,23 +1,28 @@
package com.jetpackduba.gitnuro.git.diff
import com.jetpackduba.gitnuro.extensions.filePath
import com.jetpackduba.gitnuro.git.DiffEntryType
import com.jetpackduba.gitnuro.git.EntryContent
import com.jetpackduba.gitnuro.git.submodules.GetSubmodulesUseCase
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import org.eclipse.jgit.api.Git
import org.eclipse.jgit.diff.DiffEntry
import org.eclipse.jgit.diff.DiffFormatter
import org.eclipse.jgit.dircache.DirCacheIterator
import org.eclipse.jgit.submodule.SubmoduleStatus
import org.eclipse.jgit.submodule.SubmoduleStatusType
import org.eclipse.jgit.treewalk.FileTreeIterator
import java.io.ByteArrayOutputStream
import java.io.InvalidObjectException
import javax.inject.Inject
class FormatDiffUseCase @Inject constructor(
private val getDiffEntryForUncommitedDiffUseCase: GetDiffEntryForUncommitedDiffUseCase,
private val canGenerateTextDiffUseCase: CanGenerateTextDiffUseCase,
private val getDiffContentUseCase: GetDiffContentUseCase,
private val formatHunksUseCase: FormatHunksUseCase,
private val getDiffContentUseCase: GetDiffContentUseCase,
private val canGenerateTextDiffUseCase: CanGenerateTextDiffUseCase,
private val getDiffEntryForUncommitedDiffUseCase: GetDiffEntryForUncommitedDiffUseCase,
private val getSubmodulesUseCase: GetSubmodulesUseCase,
) {
suspend operator fun invoke(
git: Git,
@ -27,6 +32,7 @@ class FormatDiffUseCase @Inject constructor(
val byteArrayOutputStream = ByteArrayOutputStream()
val repository = git.repository
val diffEntry: DiffEntry
val submodules = getSubmodulesUseCase(git)
DiffFormatter(byteArrayOutputStream).use { formatter ->
formatter.setRepository(repository)
@ -51,6 +57,12 @@ class FormatDiffUseCase @Inject constructor(
formatter.flush()
}
var diffResult: DiffResult
val submoduleStatus = submodules[diffEntry.filePath]
if (submoduleStatus != null) {
diffResult = DiffResult.Submodule(diffEntry, submoduleStatus)
} else {
val oldTree: DirCacheIterator?
val newTree: FileTreeIterator?
@ -68,21 +80,27 @@ class FormatDiffUseCase @Inject constructor(
val rawOld = diffContent.rawOld
val rawNew = diffContent.rawNew
if (rawOld == EntryContent.InvalidObjectBlob || rawNew == EntryContent.InvalidObjectBlob)
if (rawOld == EntryContent.InvalidObjectBlob || rawNew == EntryContent.InvalidObjectBlob) {
throw InvalidObjectException("Invalid object in diff format")
var diffResult: DiffResult = DiffResult.Text(diffEntry, emptyList())
} else if (rawOld == EntryContent.Submodule || rawNew == EntryContent.Submodule) {
diffResult = DiffResult.Submodule(diffEntry, null)
} else {
diffResult = DiffResult.Text(diffEntry, emptyList())
// If we can, generate text diff (if one of the files has never been a binary file)
val hasGeneratedTextDiff = canGenerateTextDiffUseCase(rawOld, rawNew) { oldRawText, newRawText ->
diffResult =
DiffResult.Text(diffEntry, formatHunksUseCase(fileHeader, oldRawText, newRawText, isDisplayFullFile))
DiffResult.Text(
diffEntry,
formatHunksUseCase(fileHeader, oldRawText, newRawText, isDisplayFullFile)
)
}
if (!hasGeneratedTextDiff) {
diffResult = DiffResult.NonText(diffEntry, rawOld, rawNew)
}
}
}
return@withContext diffResult
}

View File

@ -176,9 +176,6 @@ fun UncommitedChanges(
)
}
// TODO After moving to compose 1.5.0 (beta and RC), the right click event stops working randomly just on
// unstaged changes. It could be related to the changes to the Popup API and the custom implementation
// that Gitnuro uses
val unstagedView: @Composable () -> Unit = {
EntriesList(
modifier = Modifier

View File

@ -56,9 +56,11 @@ fun submoduleContextMenuItems(
// )
}
if(isNotEmpty()) {
add(
ContextMenuElement.ContextSeparator,
)
}
add(
ContextMenuElement.ContextTextEntry(

View File

@ -35,6 +35,7 @@ import androidx.compose.ui.platform.PlatformLocalization
import androidx.compose.ui.res.loadImageBitmap
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.text.SpanStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
@ -53,6 +54,7 @@ import com.jetpackduba.gitnuro.git.workspace.StatusType
import com.jetpackduba.gitnuro.keybindings.KeybindingOption
import com.jetpackduba.gitnuro.keybindings.matchesBinding
import com.jetpackduba.gitnuro.theme.*
import com.jetpackduba.gitnuro.ui.components.PrimaryButton
import com.jetpackduba.gitnuro.ui.components.ScrollableLazyColumn
import com.jetpackduba.gitnuro.ui.components.SecondaryButton
import com.jetpackduba.gitnuro.ui.components.Tooltip
@ -65,6 +67,7 @@ import com.jetpackduba.gitnuro.viewmodels.ViewDiffResult
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import org.eclipse.jgit.diff.DiffEntry
import org.eclipse.jgit.submodule.SubmoduleStatusType
import org.jetbrains.compose.animatedimage.Blank
import org.jetbrains.compose.animatedimage.animate
import org.jetbrains.compose.animatedimage.loadAnimatedImage
@ -118,6 +121,7 @@ fun Diff(
diffEntry = diffEntry,
onCloseDiffView = onCloseDiffView,
diffType = diffType,
isTextDiff = diffResult is DiffResult.Text,
isDisplayFullFile = isDisplayFullFile,
onStageFile = { diffViewModel.stageFile(it) },
onUnstageFile = { diffViewModel.unstageFile(it) },
@ -181,6 +185,13 @@ fun Diff(
diffResult,
onOpenFileWithExternalApp = { path -> diffViewModel.openFileWithExternalApp(path) })
}
is DiffResult.Submodule -> {
SubmoduleDiff(
diffResult,
onOpenSubmodule = { diffViewModel.openSubmodule(diffResult.diffEntry.filePath) }
)
}
}
}
@ -259,6 +270,60 @@ fun NonTextDiff(diffResult: DiffResult.NonText, onOpenFileWithExternalApp: (Stri
}
}
@Composable
fun SubmoduleDiff(diffResult: DiffResult.Submodule, onOpenSubmodule: () -> Unit) {
Column(
modifier = Modifier.fillMaxSize(),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally,
) {
Text(
text = "Submodule ${diffResult.diffEntry.filePath}",
style = MaterialTheme.typography.h4,
modifier = Modifier.padding(bottom = 8.dp),
fontWeight = FontWeight.SemiBold,
color = MaterialTheme.colors.onBackground,
)
SelectionContainer {
Column {
Text(
AnnotatedString(
"Old ID: ",
SpanStyle(fontWeight = FontWeight.SemiBold)
) + AnnotatedString(diffResult.diffEntry.oldId.name()),
color = MaterialTheme.colors.onBackground,
)
Text(
AnnotatedString(
"New ID: ",
SpanStyle(fontWeight = FontWeight.SemiBold)
) + AnnotatedString(diffResult.diffEntry.newId.name()),
color = MaterialTheme.colors.onBackground,
)
}
}
val submoduleStatus = diffResult.submoduleStatus
if (
submoduleStatus != null &&
listOf(
SubmoduleStatusType.INITIALIZED,
SubmoduleStatusType.REV_CHECKED_OUT
).contains(submoduleStatus.type) &&
submoduleStatus.indexId == diffResult.diffEntry.newId?.toObjectId()
) {
PrimaryButton(
modifier = Modifier.padding(top = 8.dp),
text = "Open submodule",
onClick = onOpenSubmodule,
)
}
}
}
@Composable
fun SideTitle(text: String) {
Text(
@ -778,6 +843,7 @@ private fun DiffHeader(
diffEntry: DiffEntry,
diffType: TextDiffType,
isDisplayFullFile: Boolean,
isTextDiff: Boolean,
onCloseDiffView: () -> Unit,
onStageFile: (StatusEntry) -> Unit,
onUnstageFile: (StatusEntry) -> Unit,
@ -837,7 +903,7 @@ private fun DiffHeader(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(16.dp)
) {
if (diffEntryType.statusType != StatusType.ADDED && diffEntryType.statusType != StatusType.REMOVED) {
if (diffEntryType.statusType != StatusType.ADDED && diffEntryType.statusType != StatusType.REMOVED && isTextDiff) {
DiffTypeButtons(
diffType = diffType,
isDisplayFullFile = isDisplayFullFile,
@ -948,36 +1014,6 @@ fun DiffTypeButtons(
onClick = { onChangeDiffType(TextDiffType.SPLIT) },
)
}
//
// Text(
// "Unified",
// color = MaterialTheme.colors.onBackground,
// style = MaterialTheme.typography.caption,
// )
//
// Switch(
// checked = diffType == TextDiffType.SPLIT,
// onCheckedChange = { checked ->
// val newType = if (checked)
// TextDiffType.SPLIT
// else
// TextDiffType.UNIFIED
//
// onChangeDiffType(newType)
// },
// colors = SwitchDefaults.colors(
// uncheckedThumbColor = MaterialTheme.colors.secondaryVariant,
// uncheckedTrackColor = MaterialTheme.colors.secondaryVariant,
// uncheckedTrackAlpha = 0.54f
// )
// )
//
// Text(
// "Split",
// color = MaterialTheme.colors.onBackground,
//// modifier = Modifier.padding(horizontal = 4.dp),
// style = MaterialTheme.typography.caption,
// )
}
}

View File

@ -11,6 +11,7 @@ import com.jetpackduba.gitnuro.git.diff.*
import com.jetpackduba.gitnuro.git.workspace.*
import com.jetpackduba.gitnuro.preferences.AppSettings
import com.jetpackduba.gitnuro.system.OpenFileInExternalAppUseCase
import com.jetpackduba.gitnuro.ui.TabsManager
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.MutableStateFlow
@ -36,6 +37,7 @@ class DiffViewModel @Inject constructor(
private val settings: AppSettings,
private val generateSplitHunkFromDiffResultUseCase: GenerateSplitHunkFromDiffResultUseCase,
private val discardUnstagedHunkLineUseCase: DiscardUnstagedHunkLineUseCase,
private val tabsManager: TabsManager,
tabScope: CoroutineScope,
) {
private val _diffResult = MutableStateFlow<ViewDiffResult>(ViewDiffResult.Loading(""))
@ -218,6 +220,10 @@ class DiffViewModel @Inject constructor(
) { git ->
discardUnstagedHunkLineUseCase(git, entry, hunk, line)
}
fun openSubmodule(path: String) = tabState.runOperation(refreshType = RefreshType.NONE) { git ->
tabsManager.addNewTabFromPath("${git.repository.workTree}/$path", true)
}
}
enum class TextDiffType(val value: Int) {