Clicking on a submodule in (un)commited changes shows an informative screen
This commit is contained in:
parent
f8a6884098
commit
5f2180f1a3
@ -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()
|
||||
|
@ -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)
|
||||
}
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -56,9 +56,11 @@ fun submoduleContextMenuItems(
|
||||
// )
|
||||
}
|
||||
|
||||
if(isNotEmpty()) {
|
||||
add(
|
||||
ContextMenuElement.ContextSeparator,
|
||||
)
|
||||
}
|
||||
|
||||
add(
|
||||
ContextMenuElement.ContextTextEntry(
|
||||
|
@ -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,
|
||||
// )
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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) {
|
||||
|
Loading…
Reference in New Issue
Block a user