parent
07bb331daf
commit
1453c6f356
@ -0,0 +1,63 @@
|
|||||||
|
package com.jetpackduba.gitnuro.git.workspace
|
||||||
|
|
||||||
|
import com.jetpackduba.gitnuro.extensions.lineDelimiter
|
||||||
|
import com.jetpackduba.gitnuro.git.diff.Hunk
|
||||||
|
import com.jetpackduba.gitnuro.git.diff.Line
|
||||||
|
import com.jetpackduba.gitnuro.git.diff.LineType
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
import org.eclipse.jgit.api.Git
|
||||||
|
import org.eclipse.jgit.diff.DiffEntry
|
||||||
|
import java.io.File
|
||||||
|
import java.io.FileWriter
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
class DiscardUnstagedHunkLineUseCase @Inject constructor(
|
||||||
|
private val getLinesFromTextUseCase: GetLinesFromTextUseCase,
|
||||||
|
) {
|
||||||
|
suspend operator fun invoke(git: Git, diffEntry: DiffEntry, hunk: Hunk, line: Line) =
|
||||||
|
withContext(Dispatchers.IO) {
|
||||||
|
val repository = git.repository
|
||||||
|
|
||||||
|
try {
|
||||||
|
val file = File(repository.directory.parent, diffEntry.oldPath)
|
||||||
|
val content = file.readText()
|
||||||
|
val textLines = getLinesFromTextUseCase(content, content.lineDelimiter).toMutableList()
|
||||||
|
|
||||||
|
if (line.lineType == LineType.ADDED) {
|
||||||
|
textLines.removeAt(line.newLineNumber)
|
||||||
|
} else if (line.lineType == LineType.REMOVED) {
|
||||||
|
val previousContextLine = hunk.lines
|
||||||
|
.takeWhile { it != line }
|
||||||
|
.lastOrNull { it.lineType == LineType.CONTEXT }
|
||||||
|
|
||||||
|
if (previousContextLine != null) {
|
||||||
|
textLines.add(previousContextLine.newLineNumber + 1, line.text)
|
||||||
|
} else {
|
||||||
|
val previousAddedLine = hunk.lines
|
||||||
|
.takeWhile { it != line }
|
||||||
|
.lastOrNull { it.lineType == LineType.ADDED }
|
||||||
|
|
||||||
|
if (previousAddedLine != null) {
|
||||||
|
textLines.add(previousAddedLine.newLineNumber + 1, line.text)
|
||||||
|
} else {
|
||||||
|
textLines.add(0, line.text)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val resultText = textLines.joinToString("")
|
||||||
|
|
||||||
|
|
||||||
|
FileWriter(file).use { fw ->
|
||||||
|
fw.write(resultText)
|
||||||
|
}
|
||||||
|
} catch (ex: Exception) {
|
||||||
|
throw Exception(
|
||||||
|
"Discard hunk line failed. Check if the file still exists and has the write permissions set",
|
||||||
|
ex
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -175,7 +175,8 @@ fun HistoryContentLoaded(
|
|||||||
onUnstageHunk = { _, _ -> },
|
onUnstageHunk = { _, _ -> },
|
||||||
onStageHunk = { _, _ -> },
|
onStageHunk = { _, _ -> },
|
||||||
onResetHunk = { _, _ -> },
|
onResetHunk = { _, _ -> },
|
||||||
onActionTriggered = { _, _, _ -> }
|
onUnStageLine = { _, _, _ -> },
|
||||||
|
onDiscardLine = { _, _, _ -> },
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -187,7 +188,8 @@ fun HistoryContentLoaded(
|
|||||||
onUnstageHunk = { _, _ -> },
|
onUnstageHunk = { _, _ -> },
|
||||||
onStageHunk = { _, _ -> },
|
onStageHunk = { _, _ -> },
|
||||||
onResetHunk = { _, _ -> },
|
onResetHunk = { _, _ -> },
|
||||||
onActionTriggered = { _, _, _ -> },
|
onUnStageLine = { _, _, _ -> },
|
||||||
|
onDiscardLine = { _, _, _ -> },
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,26 @@
|
|||||||
|
package com.jetpackduba.gitnuro.ui.context_menu
|
||||||
|
|
||||||
|
import androidx.compose.foundation.ContextMenuState
|
||||||
|
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||||
|
import androidx.compose.foundation.text.TextContextMenu
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.text.AnnotatedString
|
||||||
|
|
||||||
|
@OptIn(ExperimentalFoundationApi::class)
|
||||||
|
class CustomTextContextMenu(val onIsTextSelected: (AnnotatedString) -> Unit) : TextContextMenu {
|
||||||
|
@Composable
|
||||||
|
override fun Area(
|
||||||
|
textManager: TextContextMenu.TextManager,
|
||||||
|
state: ContextMenuState,
|
||||||
|
content: @Composable () -> Unit
|
||||||
|
) {
|
||||||
|
try {
|
||||||
|
// For some reason, compose crashes internally when calling selectedText the first time Area is composed
|
||||||
|
onIsTextSelected(textManager.selectedText)
|
||||||
|
} catch (ex: Exception) {
|
||||||
|
println("Selected text check failed " + ex.message)
|
||||||
|
}
|
||||||
|
|
||||||
|
content()
|
||||||
|
}
|
||||||
|
}
|
@ -9,6 +9,7 @@ import androidx.compose.foundation.layout.*
|
|||||||
import androidx.compose.foundation.lazy.LazyListState
|
import androidx.compose.foundation.lazy.LazyListState
|
||||||
import androidx.compose.foundation.lazy.items
|
import androidx.compose.foundation.lazy.items
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
|
import androidx.compose.foundation.text.LocalTextContextMenu
|
||||||
import androidx.compose.foundation.text.selection.DisableSelection
|
import androidx.compose.foundation.text.selection.DisableSelection
|
||||||
import androidx.compose.foundation.text.selection.SelectionContainer
|
import androidx.compose.foundation.text.selection.SelectionContainer
|
||||||
import androidx.compose.material.*
|
import androidx.compose.material.*
|
||||||
@ -27,8 +28,13 @@ import androidx.compose.ui.input.key.onPreviewKeyEvent
|
|||||||
import androidx.compose.ui.input.key.type
|
import androidx.compose.ui.input.key.type
|
||||||
import androidx.compose.ui.input.pointer.PointerEventType
|
import androidx.compose.ui.input.pointer.PointerEventType
|
||||||
import androidx.compose.ui.input.pointer.onPointerEvent
|
import androidx.compose.ui.input.pointer.onPointerEvent
|
||||||
|
import androidx.compose.ui.platform.ClipboardManager
|
||||||
|
import androidx.compose.ui.platform.LocalClipboardManager
|
||||||
|
import androidx.compose.ui.platform.LocalLocalization
|
||||||
|
import androidx.compose.ui.platform.PlatformLocalization
|
||||||
import androidx.compose.ui.res.loadImageBitmap
|
import androidx.compose.ui.res.loadImageBitmap
|
||||||
import androidx.compose.ui.res.painterResource
|
import androidx.compose.ui.res.painterResource
|
||||||
|
import androidx.compose.ui.text.AnnotatedString
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
import androidx.compose.ui.text.style.TextOverflow
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
@ -49,6 +55,9 @@ import com.jetpackduba.gitnuro.keybindings.matchesBinding
|
|||||||
import com.jetpackduba.gitnuro.theme.*
|
import com.jetpackduba.gitnuro.theme.*
|
||||||
import com.jetpackduba.gitnuro.ui.components.ScrollableLazyColumn
|
import com.jetpackduba.gitnuro.ui.components.ScrollableLazyColumn
|
||||||
import com.jetpackduba.gitnuro.ui.components.SecondaryButton
|
import com.jetpackduba.gitnuro.ui.components.SecondaryButton
|
||||||
|
import com.jetpackduba.gitnuro.ui.context_menu.ContextMenu
|
||||||
|
import com.jetpackduba.gitnuro.ui.context_menu.ContextMenuElement
|
||||||
|
import com.jetpackduba.gitnuro.ui.context_menu.CustomTextContextMenu
|
||||||
import com.jetpackduba.gitnuro.viewmodels.DiffViewModel
|
import com.jetpackduba.gitnuro.viewmodels.DiffViewModel
|
||||||
import com.jetpackduba.gitnuro.viewmodels.TextDiffType
|
import com.jetpackduba.gitnuro.viewmodels.TextDiffType
|
||||||
import com.jetpackduba.gitnuro.viewmodels.ViewDiffResult
|
import com.jetpackduba.gitnuro.viewmodels.ViewDiffResult
|
||||||
@ -128,11 +137,14 @@ fun Diff(
|
|||||||
onResetHunk = { entry, hunk ->
|
onResetHunk = { entry, hunk ->
|
||||||
diffViewModel.resetHunk(entry, hunk)
|
diffViewModel.resetHunk(entry, hunk)
|
||||||
},
|
},
|
||||||
onActionTriggered = { entry, hunk, line ->
|
onUnStageLine = { entry, hunk, line ->
|
||||||
if (diffEntryType is DiffEntryType.UnstagedDiff)
|
if (diffEntryType is DiffEntryType.UnstagedDiff)
|
||||||
diffViewModel.stageHunkLine(entry, hunk, line)
|
diffViewModel.stageHunkLine(entry, hunk, line)
|
||||||
else if (diffEntryType is DiffEntryType.StagedDiff)
|
else if (diffEntryType is DiffEntryType.StagedDiff)
|
||||||
diffViewModel.unstageHunkLine(entry, hunk, line)
|
diffViewModel.unstageHunkLine(entry, hunk, line)
|
||||||
|
},
|
||||||
|
onDiscardLine = { entry, hunk, line ->
|
||||||
|
diffViewModel.discardHunkLine(entry, hunk, line)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -149,16 +161,21 @@ fun Diff(
|
|||||||
onResetHunk = { entry, hunk ->
|
onResetHunk = { entry, hunk ->
|
||||||
diffViewModel.resetHunk(entry, hunk)
|
diffViewModel.resetHunk(entry, hunk)
|
||||||
},
|
},
|
||||||
onActionTriggered = { entry, hunk, line ->
|
onUnStageLine = { entry, hunk, line ->
|
||||||
if (diffEntryType is DiffEntryType.UnstagedDiff)
|
if (diffEntryType is DiffEntryType.UnstagedDiff)
|
||||||
diffViewModel.stageHunkLine(entry, hunk, line)
|
diffViewModel.stageHunkLine(entry, hunk, line)
|
||||||
else if (diffEntryType is DiffEntryType.StagedDiff)
|
else if (diffEntryType is DiffEntryType.StagedDiff)
|
||||||
diffViewModel.unstageHunkLine(entry, hunk, line)
|
diffViewModel.unstageHunkLine(entry, hunk, line)
|
||||||
|
},
|
||||||
|
onDiscardLine = { entry, hunk, line ->
|
||||||
|
diffViewModel.discardHunkLine(entry, hunk, line)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
is DiffResult.NonText -> {
|
is DiffResult.NonText -> {
|
||||||
NonTextDiff(diffResult, onOpenFileWithExternalApp = { path -> diffViewModel.openFileWithExternalApp(path) })
|
NonTextDiff(
|
||||||
|
diffResult,
|
||||||
|
onOpenFileWithExternalApp = { path -> diffViewModel.openFileWithExternalApp(path) })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -256,6 +273,7 @@ fun SideDiff(entryContent: EntryContent, onOpenFileWithExternalApp: (String) ->
|
|||||||
entryContent.contentType,
|
entryContent.contentType,
|
||||||
onOpenFileWithExternalApp = { onOpenFileWithExternalApp(entryContent.imagePath) }
|
onOpenFileWithExternalApp = { onOpenFileWithExternalApp(entryContent.imagePath) }
|
||||||
)
|
)
|
||||||
|
|
||||||
else -> {
|
else -> {
|
||||||
}
|
}
|
||||||
// is EntryContent.Text -> //TODO maybe have a text view if the file was a binary before?
|
// is EntryContent.Text -> //TODO maybe have a text view if the file was a binary before?
|
||||||
@ -337,6 +355,7 @@ fun BinaryDiff() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@OptIn(ExperimentalFoundationApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun HunkUnifiedTextDiff(
|
fun HunkUnifiedTextDiff(
|
||||||
diffEntryType: DiffEntryType,
|
diffEntryType: DiffEntryType,
|
||||||
@ -344,11 +363,20 @@ fun HunkUnifiedTextDiff(
|
|||||||
diffResult: DiffResult.Text,
|
diffResult: DiffResult.Text,
|
||||||
onUnstageHunk: (DiffEntry, Hunk) -> Unit,
|
onUnstageHunk: (DiffEntry, Hunk) -> Unit,
|
||||||
onStageHunk: (DiffEntry, Hunk) -> Unit,
|
onStageHunk: (DiffEntry, Hunk) -> Unit,
|
||||||
onActionTriggered: (DiffEntry, Hunk, Line) -> Unit,
|
onUnStageLine: (DiffEntry, Hunk, Line) -> Unit,
|
||||||
onResetHunk: (DiffEntry, Hunk) -> Unit,
|
onResetHunk: (DiffEntry, Hunk) -> Unit,
|
||||||
|
onDiscardLine: (DiffEntry, Hunk, Line) -> Unit,
|
||||||
) {
|
) {
|
||||||
val hunks = diffResult.hunks
|
val hunks = diffResult.hunks
|
||||||
|
var selectedText by remember { mutableStateOf(AnnotatedString("")) }
|
||||||
|
val localClipboardManager = LocalClipboardManager.current
|
||||||
|
val localization = LocalLocalization.current
|
||||||
|
|
||||||
|
CompositionLocalProvider(
|
||||||
|
LocalTextContextMenu provides CustomTextContextMenu {
|
||||||
|
selectedText = it
|
||||||
|
}
|
||||||
|
) {
|
||||||
SelectionContainer {
|
SelectionContainer {
|
||||||
ScrollableLazyColumn(
|
ScrollableLazyColumn(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
@ -374,12 +402,20 @@ fun HunkUnifiedTextDiff(
|
|||||||
val highestLineNumberLength = highestLineNumber.toString().count()
|
val highestLineNumberLength = highestLineNumber.toString().count()
|
||||||
|
|
||||||
items(hunk.lines) { line ->
|
items(hunk.lines) { line ->
|
||||||
|
DiffContextMenu(
|
||||||
|
selectedText = selectedText,
|
||||||
|
localization = localization,
|
||||||
|
localClipboardManager = localClipboardManager,
|
||||||
|
diffEntryType = diffEntryType,
|
||||||
|
onDiscardLine = { onDiscardLine(diffResult.diffEntry, hunk, line) },
|
||||||
|
line = line,
|
||||||
|
) {
|
||||||
DiffLine(
|
DiffLine(
|
||||||
highestLineNumberLength,
|
highestLineNumberLength,
|
||||||
line,
|
line,
|
||||||
diffEntryType = diffEntryType,
|
diffEntryType = diffEntryType,
|
||||||
onActionTriggered = {
|
onActionTriggered = {
|
||||||
onActionTriggered(
|
onUnStageLine(
|
||||||
diffResult.diffEntry,
|
diffResult.diffEntry,
|
||||||
hunk,
|
hunk,
|
||||||
line,
|
line,
|
||||||
@ -390,9 +426,11 @@ fun HunkUnifiedTextDiff(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@OptIn(ExperimentalFoundationApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun HunkSplitTextDiff(
|
fun HunkSplitTextDiff(
|
||||||
diffEntryType: DiffEntryType,
|
diffEntryType: DiffEntryType,
|
||||||
@ -401,7 +439,8 @@ fun HunkSplitTextDiff(
|
|||||||
onUnstageHunk: (DiffEntry, Hunk) -> Unit,
|
onUnstageHunk: (DiffEntry, Hunk) -> Unit,
|
||||||
onStageHunk: (DiffEntry, Hunk) -> Unit,
|
onStageHunk: (DiffEntry, Hunk) -> Unit,
|
||||||
onResetHunk: (DiffEntry, Hunk) -> Unit,
|
onResetHunk: (DiffEntry, Hunk) -> Unit,
|
||||||
onActionTriggered: (DiffEntry, Hunk, Line) -> Unit,
|
onUnStageLine: (DiffEntry, Hunk, Line) -> Unit,
|
||||||
|
onDiscardLine: (DiffEntry, Hunk, Line) -> Unit,
|
||||||
) {
|
) {
|
||||||
val hunks = diffResult.hunks
|
val hunks = diffResult.hunks
|
||||||
|
|
||||||
@ -410,6 +449,13 @@ fun HunkSplitTextDiff(
|
|||||||
*/
|
*/
|
||||||
var selectableSide by remember { mutableStateOf(SelectableSide.BOTH) }
|
var selectableSide by remember { mutableStateOf(SelectableSide.BOTH) }
|
||||||
|
|
||||||
|
var selectedText by remember { mutableStateOf(AnnotatedString("")) }
|
||||||
|
|
||||||
|
CompositionLocalProvider(
|
||||||
|
LocalTextContextMenu provides CustomTextContextMenu {
|
||||||
|
selectedText = it
|
||||||
|
}
|
||||||
|
) {
|
||||||
SelectionContainer {
|
SelectionContainer {
|
||||||
ScrollableLazyColumn(
|
ScrollableLazyColumn(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
@ -441,19 +487,24 @@ fun HunkSplitTextDiff(
|
|||||||
newLine = linesPair.second,
|
newLine = linesPair.second,
|
||||||
selectableSide = selectableSide,
|
selectableSide = selectableSide,
|
||||||
diffEntryType = diffEntryType,
|
diffEntryType = diffEntryType,
|
||||||
|
selectedText = selectedText,
|
||||||
onActionTriggered = { line ->
|
onActionTriggered = { line ->
|
||||||
onActionTriggered(diffResult.diffEntry, splitHunk.sourceHunk, line)
|
onUnStageLine(diffResult.diffEntry, splitHunk.sourceHunk, line)
|
||||||
},
|
},
|
||||||
onChangeSelectableSide = { newSelectableSide ->
|
onChangeSelectableSide = { newSelectableSide ->
|
||||||
if (newSelectableSide != selectableSide) {
|
if (newSelectableSide != selectableSide) {
|
||||||
selectableSide = newSelectableSide
|
selectableSide = newSelectableSide
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
onDiscardLine = { line ->
|
||||||
|
onDiscardLine(diffResult.diffEntry, splitHunk.sourceHunk, line)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
@ -471,8 +522,10 @@ fun SplitDiffLine(
|
|||||||
newLine: Line?,
|
newLine: Line?,
|
||||||
selectableSide: SelectableSide,
|
selectableSide: SelectableSide,
|
||||||
diffEntryType: DiffEntryType,
|
diffEntryType: DiffEntryType,
|
||||||
|
selectedText: AnnotatedString,
|
||||||
onChangeSelectableSide: (SelectableSide) -> Unit,
|
onChangeSelectableSide: (SelectableSide) -> Unit,
|
||||||
onActionTriggered: (Line) -> Unit,
|
onActionTriggered: (Line) -> Unit,
|
||||||
|
onDiscardLine: (Line) -> Unit,
|
||||||
) {
|
) {
|
||||||
Row(
|
Row(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
@ -489,7 +542,9 @@ fun SplitDiffLine(
|
|||||||
lineSelectableSide = SelectableSide.OLD,
|
lineSelectableSide = SelectableSide.OLD,
|
||||||
onChangeSelectableSide = onChangeSelectableSide,
|
onChangeSelectableSide = onChangeSelectableSide,
|
||||||
diffEntryType = diffEntryType,
|
diffEntryType = diffEntryType,
|
||||||
onActionTriggered = { if (oldLine != null) onActionTriggered(oldLine) }
|
onActionTriggered = { if (oldLine != null) onActionTriggered(oldLine) },
|
||||||
|
selectedText = selectedText,
|
||||||
|
onDiscardLine = onDiscardLine,
|
||||||
)
|
)
|
||||||
|
|
||||||
Box(
|
Box(
|
||||||
@ -509,7 +564,9 @@ fun SplitDiffLine(
|
|||||||
lineSelectableSide = SelectableSide.NEW,
|
lineSelectableSide = SelectableSide.NEW,
|
||||||
onChangeSelectableSide = onChangeSelectableSide,
|
onChangeSelectableSide = onChangeSelectableSide,
|
||||||
diffEntryType = diffEntryType,
|
diffEntryType = diffEntryType,
|
||||||
onActionTriggered = { if (newLine != null) onActionTriggered(newLine) }
|
onActionTriggered = { if (newLine != null) onActionTriggered(newLine) },
|
||||||
|
selectedText = selectedText,
|
||||||
|
onDiscardLine = onDiscardLine,
|
||||||
)
|
)
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -525,11 +582,18 @@ fun SplitDiffLineSide(
|
|||||||
currentSelectableSide: SelectableSide,
|
currentSelectableSide: SelectableSide,
|
||||||
lineSelectableSide: SelectableSide,
|
lineSelectableSide: SelectableSide,
|
||||||
diffEntryType: DiffEntryType,
|
diffEntryType: DiffEntryType,
|
||||||
|
selectedText: AnnotatedString,
|
||||||
onChangeSelectableSide: (SelectableSide) -> Unit,
|
onChangeSelectableSide: (SelectableSide) -> Unit,
|
||||||
onActionTriggered: () -> Unit,
|
onActionTriggered: () -> Unit,
|
||||||
|
onDiscardLine: (Line) -> Unit,
|
||||||
) {
|
) {
|
||||||
var pressedAndMoved by remember(line) { mutableStateOf(Pair(false, false)) }
|
var pressedAndMoved by remember(line) { mutableStateOf(Pair(false, false)) }
|
||||||
var movesCount by remember(line) { mutableStateOf(0) }
|
var movesCount by remember(line) { mutableStateOf(0) }
|
||||||
|
|
||||||
|
val localClipboardManager = LocalClipboardManager.current
|
||||||
|
val localization = LocalLocalization.current
|
||||||
|
|
||||||
|
|
||||||
Box(
|
Box(
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
.onPointerEvent(PointerEventType.Press) {
|
.onPointerEvent(PointerEventType.Press) {
|
||||||
@ -563,6 +627,14 @@ fun SplitDiffLineSide(
|
|||||||
DynamicSelectionDisable(
|
DynamicSelectionDisable(
|
||||||
currentSelectableSide != lineSelectableSide &&
|
currentSelectableSide != lineSelectableSide &&
|
||||||
currentSelectableSide != SelectableSide.BOTH
|
currentSelectableSide != SelectableSide.BOTH
|
||||||
|
) {
|
||||||
|
DiffContextMenu(
|
||||||
|
selectedText,
|
||||||
|
localization,
|
||||||
|
localClipboardManager,
|
||||||
|
line,
|
||||||
|
diffEntryType,
|
||||||
|
onDiscardLine = { onDiscardLine(line) },
|
||||||
) {
|
) {
|
||||||
SplitDiffLine(
|
SplitDiffLine(
|
||||||
highestLineNumberLength = highestLineNumberLength,
|
highestLineNumberLength = highestLineNumberLength,
|
||||||
@ -574,6 +646,55 @@ fun SplitDiffLineSide(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun DiffContextMenu(
|
||||||
|
selectedText: AnnotatedString,
|
||||||
|
localization: PlatformLocalization,
|
||||||
|
localClipboardManager: ClipboardManager,
|
||||||
|
line: Line,
|
||||||
|
diffEntryType: DiffEntryType,
|
||||||
|
onDiscardLine: () -> Unit,
|
||||||
|
content: @Composable () -> Unit,
|
||||||
|
) {
|
||||||
|
ContextMenu(
|
||||||
|
items = {
|
||||||
|
val isTextSelected = selectedText.isNotEmpty()
|
||||||
|
|
||||||
|
if (isTextSelected) {
|
||||||
|
listOf(
|
||||||
|
ContextMenuElement.ContextTextEntry(
|
||||||
|
label = localization.copy,
|
||||||
|
icon = { painterResource(AppIcons.COPY) },
|
||||||
|
onClick = {
|
||||||
|
localClipboardManager.setText(selectedText)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
if (
|
||||||
|
line.lineType != LineType.CONTEXT &&
|
||||||
|
diffEntryType is DiffEntryType.UnstagedDiff &&
|
||||||
|
diffEntryType.statusType == StatusType.MODIFIED
|
||||||
|
) {
|
||||||
|
listOf(
|
||||||
|
ContextMenuElement.ContextTextEntry(
|
||||||
|
label = "Discard line",
|
||||||
|
icon = { painterResource(AppIcons.UNDO) },
|
||||||
|
onClick = {
|
||||||
|
onDiscardLine()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
} else
|
||||||
|
emptyList()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
) {
|
||||||
|
content()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
enum class SelectableSide {
|
enum class SelectableSide {
|
||||||
|
@ -34,6 +34,7 @@ class DiffViewModel @Inject constructor(
|
|||||||
private val openFileInExternalAppUseCase: OpenFileInExternalAppUseCase,
|
private val openFileInExternalAppUseCase: OpenFileInExternalAppUseCase,
|
||||||
private val settings: AppSettings,
|
private val settings: AppSettings,
|
||||||
private val generateSplitHunkFromDiffResultUseCase: GenerateSplitHunkFromDiffResultUseCase,
|
private val generateSplitHunkFromDiffResultUseCase: GenerateSplitHunkFromDiffResultUseCase,
|
||||||
|
private val discardUnstagedHunkLineUseCase: DiscardUnstagedHunkLineUseCase,
|
||||||
tabScope: CoroutineScope,
|
tabScope: CoroutineScope,
|
||||||
) {
|
) {
|
||||||
private val _diffResult = MutableStateFlow<ViewDiffResult>(ViewDiffResult.Loading(""))
|
private val _diffResult = MutableStateFlow<ViewDiffResult>(ViewDiffResult.Loading(""))
|
||||||
@ -192,6 +193,13 @@ class DiffViewModel @Inject constructor(
|
|||||||
fun openFileWithExternalApp(path: String) {
|
fun openFileWithExternalApp(path: String) {
|
||||||
openFileInExternalAppUseCase(path)
|
openFileInExternalAppUseCase(path)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun discardHunkLine(entry: DiffEntry, hunk: Hunk, line: Line) = tabState.runOperation(
|
||||||
|
refreshType = RefreshType.UNCOMMITED_CHANGES,
|
||||||
|
showError = true,
|
||||||
|
) { git ->
|
||||||
|
discardUnstagedHunkLineUseCase(git, entry, hunk, line)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
enum class TextDiffType(val value: Int) {
|
enum class TextDiffType(val value: Int) {
|
||||||
|
Loading…
Reference in New Issue
Block a user