diff --git a/src/main/kotlin/app/git/StatusManager.kt b/src/main/kotlin/app/git/StatusManager.kt index 0622ef6..d6d8cee 100644 --- a/src/main/kotlin/app/git/StatusManager.kt +++ b/src/main/kotlin/app/git/StatusManager.kt @@ -1,12 +1,16 @@ package app.git +import androidx.compose.material.MaterialTheme +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Warning +import androidx.compose.runtime.Composable +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.vector.ImageVector import app.di.RawFileManagerFactory -import app.extensions.filePath -import app.extensions.hasUntrackedChanges -import app.extensions.isMerging -import app.extensions.withoutLineEnding +import app.extensions.* import app.git.diff.Hunk import app.git.diff.LineType +import app.theme.conflictFile import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.ensureActive import kotlinx.coroutines.flow.MutableStateFlow @@ -66,38 +70,53 @@ class StatusManager @Inject constructor( loadHasUncommitedChanges(git) val currentBranch = branchesManager.currentBranchRef(git) val repositoryState = git.repository.repositoryState - val staged = git.diff().apply { - if (currentBranch == null && !repositoryState.isMerging && !repositoryState.isRebasing) - setOldTree(EmptyTreeIterator()) // Required if the repository is empty - setCached(true) - } + val staged = git + .diff() + .setShowNameAndStatusOnly(true).apply { + if (currentBranch == null && !repositoryState.isMerging && !repositoryState.isRebasing) + setOldTree(EmptyTreeIterator()) // Required if the repository is empty + + setCached(true) + } .call() // TODO: Grouping and fitlering allows us to remove duplicates when conflicts appear, requires more testing (what happens in windows? /dev/null is a unix thing) - .groupBy { it.oldPath } + // TODO: Test if we should group by old path or new path + .groupBy { + if(it.newPath != "/dev/null") + it.newPath + else + it.oldPath + } .map { val entries = it.value - if (entries.count() > 1 && (repositoryState.isMerging || repositoryState.isRebasing)) - entries.filter { entry -> entry.oldPath != "/dev/null" } - else - entries - }.flatten() + val hasConflicts = + (entries.count() > 1 && (repositoryState.isMerging || repositoryState.isRebasing)) + + StatusEntry(entries.first(), isConflict = hasConflicts) + } ensureActive() val unstaged = git .diff() + .setShowNameAndStatusOnly(true) .call() - .groupBy { it.oldPath } + .groupBy { + if(it.oldPath != "/dev/null") + it.oldPath + else + it.newPath + } .map { val entries = it.value - if (entries.count() > 1 && (repositoryState.isMerging || repositoryState.isRebasing)) - entries.filter { entry -> entry.newPath != "/dev/null" } - else - entries - }.flatten() + val hasConflicts = + (entries.count() > 1 && (repositoryState.isMerging || repositoryState.isRebasing)) + + StatusEntry(entries.first(), isConflict = hasConflicts) + } ensureActive() _stageStatus.value = StageStatus.Loaded(staged, unstaged) @@ -209,7 +228,7 @@ class StatusManager @Inject constructor( loadStatus(git) } finally { - if(completedWithErrors) + if (completedWithErrors) dirCache.unlock() } } @@ -301,5 +320,23 @@ class StatusManager @Inject constructor( sealed class StageStatus { object Loading : StageStatus() - data class Loaded(val staged: List, val unstaged: List) : StageStatus() + data class Loaded(val staged: List, val unstaged: List) : StageStatus() +} + +data class StatusEntry(val diffEntry: DiffEntry, val isConflict: Boolean) { + val icon: ImageVector + get() { + return if (isConflict) + Icons.Default.Warning + else + diffEntry.icon + } + val iconColor: Color + @Composable + get() { + return if (isConflict) + MaterialTheme.colors.conflictFile + else + diffEntry.iconColor + } } \ No newline at end of file diff --git a/src/main/kotlin/app/theme/Color.kt b/src/main/kotlin/app/theme/Color.kt index 66313ea..58f36b0 100644 --- a/src/main/kotlin/app/theme/Color.kt +++ b/src/main/kotlin/app/theme/Color.kt @@ -24,6 +24,7 @@ val headerBackgroundDark = Color(0xFF303132) val addFileLight = Color(0xFF32A852) val deleteFileLight = errorColor val modifyFileLight = primary +val conflictFileLight = Color(0xFFFFB638) val tabColorActiveDark = Color(0xFF606061) val tabColorInactiveDark = Color(0xFF262626) diff --git a/src/main/kotlin/app/theme/Theme.kt b/src/main/kotlin/app/theme/Theme.kt index a5c0cb8..05b66b3 100644 --- a/src/main/kotlin/app/theme/Theme.kt +++ b/src/main/kotlin/app/theme/Theme.kt @@ -85,6 +85,10 @@ val Colors.deleteFile: Color val Colors.modifyFile: Color get() = modifyFileLight +@get:Composable +val Colors.conflictFile: Color + get() = conflictFileLight + @get:Composable val Colors.headerText: Color get() = if (isLight) primary else mainTextDark diff --git a/src/main/kotlin/app/ui/UncommitedChanges.kt b/src/main/kotlin/app/ui/UncommitedChanges.kt index 582f669..dc21225 100644 --- a/src/main/kotlin/app/ui/UncommitedChanges.kt +++ b/src/main/kotlin/app/ui/UncommitedChanges.kt @@ -30,9 +30,11 @@ import androidx.compose.ui.unit.sp import app.extensions.filePath import app.extensions.icon import app.extensions.iconColor +import app.extensions.isMerging import app.git.DiffEntryType import app.git.GitManager import app.git.StageStatus +import app.git.StatusEntry import app.theme.headerBackground import app.theme.headerText import app.theme.primaryTextColor @@ -57,8 +59,8 @@ fun UncommitedChanges( gitManager.loadStatus() } - val staged: List - val unstaged: List + val staged: List + val unstaged: List if (stageStatus is StageStatus.Loaded) { staged = stageStatus.staged unstaged = stageStatus.unstaged @@ -74,8 +76,8 @@ fun UncommitedChanges( } } } else { - staged = listOf() - unstaged = listOf() // return empty lists if still loading + staged = listOf() + unstaged = listOf() // return empty lists if still loading } @@ -178,8 +180,14 @@ fun UncommitedChanges( enabled = canCommit, shape = RectangleShape, ) { + val buttonText = if(repositoryState.isMerging) + "Merge" + else if (repositoryState.isRebasing) + "Continue rebasing" + else + "Commit" Text( - text = "Commit", + text = buttonText, fontSize = 14.sp, ) } @@ -190,8 +198,8 @@ fun UncommitedChanges( fun checkIfSelectedEntryShouldBeUpdated( selectedEntryType: DiffEntryType, - staged: List, - unstaged: List, + staged: List, + unstaged: List, onStagedDiffEntrySelected: (DiffEntry?) -> Unit, onUnstagedDiffEntrySelected: (DiffEntry) -> Unit, ) { @@ -199,26 +207,29 @@ fun checkIfSelectedEntryShouldBeUpdated( val selectedEntryTypeNewId = selectedDiffEntry.newId.name() if (selectedEntryType is DiffEntryType.StagedDiff) { - val entryType = staged.firstOrNull { it.newPath == selectedDiffEntry.newPath } + val entryType = staged.firstOrNull { stagedEntry -> stagedEntry.diffEntry.newPath == selectedDiffEntry.newPath }?.diffEntry if( entryType != null && selectedEntryTypeNewId != entryType.newId.name() ) { onStagedDiffEntrySelected(entryType) - } else if (entryType == null) + + } else if (entryType == null) { onStagedDiffEntrySelected(null) + } } else if(selectedEntryType is DiffEntryType.UnstagedDiff) { - val entryType = unstaged.firstOrNull { + val entryType = unstaged.firstOrNull { unstagedEntry -> if(selectedDiffEntry.changeType == DiffEntry.ChangeType.DELETE) - it.oldPath == selectedDiffEntry.oldPath + unstagedEntry.diffEntry.oldPath == selectedDiffEntry.oldPath else - it.newPath == selectedDiffEntry.newPath + unstagedEntry.diffEntry.newPath == selectedDiffEntry.newPath } if(entryType != null) { - onUnstagedDiffEntrySelected(entryType) - } else onStagedDiffEntrySelected(null) + onUnstagedDiffEntrySelected(entryType.diffEntry) + } else + onStagedDiffEntrySelected(null) } } @@ -229,7 +240,7 @@ private fun EntriesList( title: String, actionTitle: String, actionColor: Color, - diffEntries: List, + diffEntries: List, onDiffEntrySelected: (DiffEntry) -> Unit, onDiffEntryOptionSelected: (DiffEntry) -> Unit, onReset: (DiffEntry) -> Unit, @@ -266,9 +277,10 @@ private fun EntriesList( .fillMaxSize() .background(MaterialTheme.colors.background), ) { - itemsIndexed(diffEntries) { index, diffEntry -> + itemsIndexed(diffEntries) { index, statusEntry -> + val diffEntry = statusEntry.diffEntry FileEntry( - diffEntry = diffEntry, + statusEntry = statusEntry, actionTitle = actionTitle, actionColor = actionColor, onClick = { @@ -296,7 +308,7 @@ private fun EntriesList( ) @Composable private fun FileEntry( - diffEntry: DiffEntry, + statusEntry: StatusEntry, actionTitle: String, actionColor: Color, onClick: () -> Unit, @@ -304,6 +316,7 @@ private fun FileEntry( onReset: () -> Unit, ) { var active by remember { mutableStateOf(false) } + val diffEntry = statusEntry.diffEntry Box( modifier = Modifier @@ -338,12 +351,12 @@ private fun FileEntry( ) { Icon( - imageVector = diffEntry.icon, + imageVector = statusEntry.icon, contentDescription = null, modifier = Modifier .padding(horizontal = 8.dp) .size(16.dp), - tint = diffEntry.iconColor, + tint = statusEntry.iconColor, ) Text(