mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
[gl] Add highlighting of comment range for GitLab's review discussions
#IJPL-220068 Fixed Space-RevId: 1a44bf21e79de563fcab08dc4629bd5c5bc13225 GitOrigin-RevId: 60e598bd75c1caa434d2a457ada7a8184894f12d
This commit is contained in:
committed by
intellij-monorepo-bot
parent
ede55ae0f7
commit
a3964f5d0d
+4
@@ -24,6 +24,10 @@ data class GitLabMergeRequestNewDiscussionPosition(
|
||||
override val filePathAfter: String? get() = paths.newPath
|
||||
override val lineIndexLeft: Int? get() = oldLineIndex
|
||||
override val lineIndexRight: Int? get() = newLineIndex
|
||||
override val startOldLine: Int? get() = lineRange?.start?.oldLine
|
||||
override val startNewLine: Int? get() = lineRange?.start?.newLine
|
||||
override val endOldLine: Int? get() = lineRange?.end?.oldLine
|
||||
override val endNewLine: Int? get() = lineRange?.end?.newLine
|
||||
|
||||
companion object {
|
||||
fun calcFor(diffData: GitTextFilePatchWithHistory, location: DiffLineLocation): GitLabMergeRequestNewDiscussionPosition {
|
||||
|
||||
+1
-1
@@ -16,7 +16,7 @@ interface GitLabMergeRequestNotePositionMapping {
|
||||
private val LOG = logger<GitLabMergeRequestNotePositionMapping>()
|
||||
|
||||
fun map(mrChanges: GitBranchComparisonResult, position: GitLabNotePosition): GitLabMergeRequestNotePositionMapping {
|
||||
val textLocation = position.getLocation(Side.LEFT)
|
||||
val textLocation = position.getLocation(Side.LEFT)?.second
|
||||
|
||||
val changes = if (position.parentSha == mrChanges.mergeBaseSha) {
|
||||
// first commit
|
||||
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
// Copyright 2000-2026 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package org.jetbrains.plugins.gitlab.mergerequest.data
|
||||
|
||||
import com.intellij.diff.util.Side
|
||||
|
||||
data class GitLabNoteLocation(
|
||||
val startSide: Side,
|
||||
val startLineIdx: Int,
|
||||
val side: Side,
|
||||
val lineIdx: Int,
|
||||
)
|
||||
+23
-6
@@ -1,7 +1,7 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package org.jetbrains.plugins.gitlab.mergerequest.data
|
||||
|
||||
import com.intellij.collaboration.ui.codereview.diff.DiffLineLocation
|
||||
import com.intellij.collaboration.ui.codereview.diff.DiffLineRange
|
||||
import com.intellij.diff.util.Side
|
||||
import com.intellij.openapi.diagnostic.logger
|
||||
import org.jetbrains.plugins.gitlab.api.dto.GitLabMergeRequestDraftNoteRestDTO
|
||||
@@ -16,6 +16,10 @@ sealed interface GitLabNotePosition {
|
||||
interface WithLine : GitLabNotePosition {
|
||||
val lineIndexLeft: Int?
|
||||
val lineIndexRight: Int?
|
||||
val startOldLine: Int?
|
||||
val startNewLine: Int?
|
||||
val endOldLine: Int?
|
||||
val endNewLine: Int?
|
||||
}
|
||||
|
||||
data class Text(
|
||||
@@ -25,6 +29,10 @@ sealed interface GitLabNotePosition {
|
||||
override val filePathAfter: String?,
|
||||
override val lineIndexLeft: Int?,
|
||||
override val lineIndexRight: Int?,
|
||||
override val startOldLine: Int?,
|
||||
override val startNewLine: Int?,
|
||||
override val endOldLine: Int?,
|
||||
override val endNewLine: Int?,
|
||||
) : WithLine
|
||||
|
||||
data class Image(
|
||||
@@ -48,7 +56,11 @@ sealed interface GitLabNotePosition {
|
||||
val sha = position.headSha
|
||||
|
||||
return when (position.positionType) {
|
||||
"text" -> Text(parentSha, sha, position.oldPath, position.newPath, position.oldLine?.dec(), position.newLine?.dec())
|
||||
"text" -> Text(parentSha, sha, position.oldPath, position.newPath,
|
||||
position.oldLine?.dec(), position.newLine?.dec(),
|
||||
position.lineRange?.start?.oldLine?.dec(), position.lineRange?.start?.newLine?.dec(),
|
||||
position.lineRange?.end?.oldLine?.dec(), position.lineRange?.end?.newLine?.dec()
|
||||
)
|
||||
else -> Image(parentSha, sha, position.oldPath, position.newPath)
|
||||
}
|
||||
}
|
||||
@@ -64,7 +76,11 @@ sealed interface GitLabNotePosition {
|
||||
val sha = position.headSha ?: return null
|
||||
|
||||
return when (position.positionType) {
|
||||
"text" -> Text(parentSha, sha, position.oldPath, position.newPath, position.oldLine?.dec(), position.newLine?.dec())
|
||||
"text" -> Text(parentSha, sha, position.oldPath, position.newPath,
|
||||
position.oldLine?.dec(), position.newLine?.dec(),
|
||||
position.lineRange?.start?.oldLine?.dec(), position.lineRange?.start?.newLine?.dec(),
|
||||
position.lineRange?.end?.oldLine?.dec(), position.lineRange?.end?.newLine?.dec()
|
||||
)
|
||||
else -> Image(parentSha, sha, position.oldPath, position.newPath)
|
||||
}
|
||||
}
|
||||
@@ -74,7 +90,8 @@ sealed interface GitLabNotePosition {
|
||||
val GitLabNotePosition.filePath: String
|
||||
get() = (filePathAfter ?: filePathBefore)!!
|
||||
|
||||
fun GitLabNotePosition.getLocation(contextSide: Side = Side.LEFT): DiffLineLocation? {
|
||||
if (this !is GitLabNotePosition.WithLine) return null
|
||||
return GitLabNotePositionUtil.getLocation(lineIndexLeft, lineIndexRight, contextSide)
|
||||
fun GitLabNotePosition.getLocation(contextSide: Side = Side.LEFT): DiffLineRange? {
|
||||
val position = this
|
||||
if (position !is GitLabNotePosition.WithLine) return null
|
||||
return GitLabNotePositionUtil.getLocation(position, contextSide)
|
||||
}
|
||||
+65
-14
@@ -2,11 +2,54 @@
|
||||
package org.jetbrains.plugins.gitlab.mergerequest.data
|
||||
|
||||
import com.intellij.collaboration.ui.codereview.diff.DiffLineLocation
|
||||
import com.intellij.collaboration.ui.codereview.diff.DiffLineRange
|
||||
import com.intellij.diff.util.Side
|
||||
import git4idea.changes.GitTextFilePatchWithHistory
|
||||
|
||||
object GitLabNotePositionUtil {
|
||||
fun getLocation(lineIndexLeft: Int?, lineIndexRight: Int?, contextSide: Side = Side.LEFT): DiffLineLocation? = when {
|
||||
fun getLocation(position: GitLabNotePosition.WithLine, contextSide: Side = Side.LEFT): DiffLineRange? =
|
||||
when {
|
||||
position.lineIndexLeft != null && position.lineIndexRight != null -> when (contextSide) {
|
||||
Side.LEFT -> getLeftSideLocation(position)
|
||||
|
||||
Side.RIGHT -> getRightSideLocation(position)
|
||||
}
|
||||
position.lineIndexLeft != null -> getLeftSideLocation(position)
|
||||
position.lineIndexRight != null -> getRightSideLocation(position)
|
||||
else -> null
|
||||
}
|
||||
|
||||
private fun getLeftSideLocation(position: GitLabNotePosition.WithLine): DiffLineRange? {
|
||||
val startLine = position.startOldLine
|
||||
val endLine = position.endOldLine
|
||||
|
||||
return if (startLine == null || endLine == null || endLine != position.lineIndexLeft) { // fallback to a single line
|
||||
getSingleLineLocation(position.lineIndexLeft, position.lineIndexRight, Side.LEFT)
|
||||
?.let { single -> DiffLineRange(single, single) }
|
||||
}
|
||||
else {
|
||||
val startLoc = DiffLineLocation(Side.LEFT, startLine)
|
||||
val endLoc = DiffLineLocation(Side.LEFT, endLine)
|
||||
DiffLineRange(startLoc, endLoc)
|
||||
}
|
||||
}
|
||||
|
||||
private fun getRightSideLocation(position: GitLabNotePosition.WithLine): DiffLineRange? {
|
||||
val startLine = position.startNewLine
|
||||
val endLine = position.endNewLine
|
||||
|
||||
return if (startLine == null || endLine == null || endLine != position.lineIndexRight) { // fallback to a single line
|
||||
getSingleLineLocation(position.lineIndexLeft, position.lineIndexRight, Side.RIGHT)
|
||||
?.let { single -> DiffLineRange(single, single) }
|
||||
}
|
||||
else {
|
||||
val startLoc = DiffLineLocation(Side.RIGHT, startLine)
|
||||
val endLoc = DiffLineLocation(Side.RIGHT, endLine)
|
||||
DiffLineRange(startLoc, endLoc)
|
||||
}
|
||||
}
|
||||
|
||||
private fun getSingleLineLocation(lineIndexLeft: Int?, lineIndexRight: Int?, contextSide: Side = Side.LEFT): DiffLineLocation? = when {
|
||||
lineIndexLeft != null && lineIndexRight != null -> when (contextSide) {
|
||||
Side.LEFT -> DiffLineLocation(Side.LEFT, lineIndexLeft)
|
||||
Side.RIGHT -> DiffLineLocation(Side.RIGHT, lineIndexRight)
|
||||
@@ -24,28 +67,36 @@ fun GitLabNotePosition.mapToRightSideLine(diffData: GitTextFilePatchWithHistory)
|
||||
mapToSidedLine(diffData, Side.RIGHT)
|
||||
|
||||
private fun GitLabNotePosition.mapToSidedLine(diffData: GitTextFilePatchWithHistory, side: Side): Int? {
|
||||
val (currentSide, lineIndex) = getLocation(side) ?: getLocation(side.other()) ?: return null
|
||||
|
||||
val (_, endLineLocation) = getLocation(side) ?: getLocation(side.other()) ?: return null
|
||||
if (!diffData.contains(parentSha, filePathBefore, sha, filePathAfter)) return null
|
||||
val revision = currentSide.select(parentSha, sha)
|
||||
|
||||
return diffData.forcefullyMapLine(revision, lineIndex, side)
|
||||
val revision = endLineLocation.first.select(parentSha, sha)
|
||||
return diffData.forcefullyMapLine(revision, endLineLocation.second, side)
|
||||
}
|
||||
|
||||
fun GitLabNotePosition.mapToLocation(diffData: GitTextFilePatchWithHistory, contextSide: Side = Side.LEFT)
|
||||
: DiffLineLocation? {
|
||||
val (side, lineIndex) = getLocation(contextSide) ?: return null
|
||||
: GitLabNoteLocation? {
|
||||
val unmappedLocation = getLocation(contextSide) ?: return null
|
||||
if (!diffData.contains(parentSha, filePathBefore, sha, filePathAfter)) return null
|
||||
val revision = side.select(parentSha, sha)
|
||||
return diffData.mapLine(revision, lineIndex, side)
|
||||
return unmappedLocation.toMapped(diffData, parentSha, sha)
|
||||
}
|
||||
|
||||
fun GitLabMergeRequestNewDiscussionPosition.mapToLocation(diffData: GitTextFilePatchWithHistory, contextSide: Side = Side.LEFT)
|
||||
: DiffLineLocation? {
|
||||
val (side, lineIndex) = GitLabNotePositionUtil.getLocation(oldLineIndex, newLineIndex, contextSide) ?: return null
|
||||
: GitLabNoteLocation? {
|
||||
val unmappedLocation = getLocation(contextSide) ?: return null
|
||||
if (!diffData.contains(baseSha, paths.oldPath, headSha, paths.newPath)) return null
|
||||
val revision = contextSide.select(baseSha, headSha)
|
||||
return diffData.mapLine(revision, lineIndex, side)
|
||||
return unmappedLocation.toMapped(diffData, baseSha, sha)
|
||||
}
|
||||
|
||||
private fun DiffLineRange.toMapped(diffData: GitTextFilePatchWithHistory, parentSha: String, sha: String): GitLabNoteLocation? {
|
||||
val (startLineLocation, endLineLocation) = this
|
||||
val startRevision = startLineLocation.first.select(parentSha, sha)
|
||||
val endRevision = endLineLocation.first.select(parentSha, sha)
|
||||
val mappedStartLine = diffData.mapLine(startRevision, startLineLocation.second, startLineLocation.first)
|
||||
val mappedEndLine = diffData.mapLine(endRevision, endLineLocation.second, endLineLocation.first)
|
||||
return if (mappedStartLine != null && mappedEndLine != null)
|
||||
GitLabNoteLocation(mappedStartLine.first, mappedStartLine.second, mappedEndLine.first, mappedEndLine.second)
|
||||
else
|
||||
mappedEndLine?.let { GitLabNoteLocation(it.first, it.second, it.first, it.second) }
|
||||
}
|
||||
|
||||
private fun GitTextFilePatchWithHistory.contains(parentSha: String, pathAtParent: String?,
|
||||
|
||||
+49
-23
@@ -11,10 +11,7 @@ import com.intellij.collaboration.async.transformConsecutiveSuccesses
|
||||
import com.intellij.collaboration.ui.codereview.diff.DiffLineLocation
|
||||
import com.intellij.collaboration.ui.codereview.diff.UnifiedCodeReviewItemPosition
|
||||
import com.intellij.collaboration.ui.codereview.diff.viewer.showCodeReview
|
||||
import com.intellij.collaboration.ui.codereview.editor.CodeReviewComponentInlayRenderer
|
||||
import com.intellij.collaboration.ui.codereview.editor.CodeReviewEditorGutterControlsModel
|
||||
import com.intellij.collaboration.ui.codereview.editor.CodeReviewEditorModel
|
||||
import com.intellij.collaboration.ui.codereview.editor.CodeReviewNavigableEditorViewModel
|
||||
import com.intellij.collaboration.ui.codereview.editor.*
|
||||
import com.intellij.collaboration.ui.icon.IconsProvider
|
||||
import com.intellij.collaboration.util.ComputedResult
|
||||
import com.intellij.collaboration.util.Hideable
|
||||
@@ -28,21 +25,20 @@ import com.intellij.diff.requests.DiffRequest
|
||||
import com.intellij.diff.tools.util.DiffNotifications
|
||||
import com.intellij.diff.tools.util.base.DiffViewerBase
|
||||
import com.intellij.diff.util.DiffUtil
|
||||
import com.intellij.diff.util.LineRange
|
||||
import com.intellij.openapi.components.Service
|
||||
import com.intellij.openapi.components.service
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.util.cancelOnDispose
|
||||
import com.intellij.util.concurrency.annotations.RequiresEdt
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.jetbrains.plugins.gitlab.GitLabSettings
|
||||
import org.jetbrains.plugins.gitlab.api.dto.GitLabUserDTO
|
||||
import org.jetbrains.plugins.gitlab.data.GitLabImageLoader
|
||||
import org.jetbrains.plugins.gitlab.mergerequest.data.GitLabNoteLocation
|
||||
import org.jetbrains.plugins.gitlab.mergerequest.ui.diff.GitLabMergeRequestDiffDiscussionViewModel
|
||||
import org.jetbrains.plugins.gitlab.mergerequest.ui.diff.GitLabMergeRequestDiffDraftNoteViewModel
|
||||
import org.jetbrains.plugins.gitlab.mergerequest.ui.diff.GitLabMergeRequestDiffNewDiscussionViewModel
|
||||
@@ -88,15 +84,26 @@ class GitLabMergeRequestDiffExtension : DiffExtension() {
|
||||
changeVm.markViewed()
|
||||
}
|
||||
|
||||
viewer.showCodeReview(
|
||||
modelFactory = { _, _, locationToLine, lineToLocations, lineToUnified ->
|
||||
DiffEditorModel(this, project, changeVm, locationToLine, lineToLocations) {
|
||||
viewer.showCodeReview { editor, _, locationToLine, lineToLocations, lineToUnified ->
|
||||
coroutineScope {
|
||||
val cs = this
|
||||
val model = DiffEditorModel(cs, project, changeVm, locationToLine, lineToLocations) {
|
||||
val (leftLine, rightLine) = lineToUnified(it)
|
||||
UnifiedCodeReviewItemPosition(change, leftLine, rightLine)
|
||||
}
|
||||
},
|
||||
rendererFactory = { createRenderer(it, changeVm.avatarIconsProvider, changeVm.imageLoader) }
|
||||
)
|
||||
val activeRangesTracker = CodeReviewActiveRangesTracker()
|
||||
editor.showCodeReview(model) { inlayModel ->
|
||||
createRenderer(inlayModel,
|
||||
changeVm.avatarIconsProvider,
|
||||
changeVm.imageLoader,
|
||||
activeRangesTracker).also { inlayRenderer ->
|
||||
launchNow {
|
||||
CodeReviewEditorInlayRangeOutlineUtils.showInlayOutline(editor, model, inlayModel, inlayRenderer, activeRangesTracker)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}.cancelOnDispose(viewer)
|
||||
@@ -106,23 +113,25 @@ class GitLabMergeRequestDiffExtension : DiffExtension() {
|
||||
model: GitLabMergeRequestEditorMappedComponentModel,
|
||||
avatarIconsProvider: IconsProvider<GitLabUserDTO>,
|
||||
imageLoader: GitLabImageLoader,
|
||||
activeRangesTracker: CodeReviewActiveRangesTracker,
|
||||
): CodeReviewComponentInlayRenderer =
|
||||
when (model) {
|
||||
is GitLabMergeRequestEditorMappedComponentModel.Discussion<*> ->
|
||||
GitLabMergeRequestDiscussionInlayRenderer(this, project, model.vm, avatarIconsProvider, imageLoader,
|
||||
GitLabMergeRequestDiscussionInlayRenderer(this, project, model, avatarIconsProvider, imageLoader, activeRangesTracker,
|
||||
GitLabStatistics.MergeRequestNoteActionPlace.DIFF)
|
||||
is GitLabMergeRequestEditorMappedComponentModel.DraftNote<*> ->
|
||||
GitLabMergeRequestDraftNoteInlayRenderer(this, project, model.vm, avatarIconsProvider, imageLoader,
|
||||
GitLabMergeRequestDraftNoteInlayRenderer(this, project, model, avatarIconsProvider, imageLoader, activeRangesTracker,
|
||||
GitLabStatistics.MergeRequestNoteActionPlace.DIFF)
|
||||
is GitLabMergeRequestEditorMappedComponentModel.NewDiscussion<*> ->
|
||||
GitLabMergeRequestNewDiscussionInlayRenderer(this, project, model.vm, avatarIconsProvider,
|
||||
GitLabMergeRequestNewDiscussionInlayRenderer(this, project, model, avatarIconsProvider, activeRangesTracker,
|
||||
GitLabStatistics.MergeRequestNoteActionPlace.DIFF, model::cancel)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal interface GitLabReviewDiffEditorModel : CodeReviewEditorModel<GitLabMergeRequestEditorMappedComponentModel>,
|
||||
CodeReviewNavigableEditorViewModel
|
||||
CodeReviewNavigableEditorViewModel,
|
||||
CodeReviewCommentableEditorModel.WithMultilineComments
|
||||
|
||||
private class DiffEditorModel(
|
||||
cs: CoroutineScope,
|
||||
@@ -152,8 +161,8 @@ private class DiffEditorModel(
|
||||
diffReviewVm.locationsWithDiscussions,
|
||||
diffReviewVm.locationsWithNewDiscussions
|
||||
) { locationsWithDiscussions, locationsWithNewDiscussions ->
|
||||
val linesWithDiscussions = locationsWithDiscussions.mapNotNullTo(mutableSetOf(), locationToLine)
|
||||
val linesWithNewDiscussions = locationsWithNewDiscussions.mapNotNullTo(mutableSetOf(), locationToLine)
|
||||
val linesWithDiscussions = locationsWithDiscussions.mapNotNullTo(mutableSetOf(), { locationToLine(it.side to it.lineIdx) })
|
||||
val linesWithNewDiscussions = locationsWithNewDiscussions.mapNotNullTo(mutableSetOf(), { locationToLine(it.side to it.lineIdx) })
|
||||
GutterState(linesWithDiscussions, linesWithNewDiscussions)
|
||||
}.stateInNow(cs, null)
|
||||
|
||||
@@ -167,6 +176,14 @@ private class DiffEditorModel(
|
||||
diffReviewVm.cancelNewDiscussion(loc)
|
||||
}
|
||||
|
||||
override fun requestNewComment(lineRange: LineRange) {
|
||||
TODO("not implemented")
|
||||
}
|
||||
|
||||
override fun canCreateComment(lineRange: LineRange): Boolean {
|
||||
TODO("not implemented")
|
||||
}
|
||||
|
||||
override fun toggleComments(lineIdx: Int) {
|
||||
inlays.value.asSequence().filter { it.line.value == lineIdx }.filterIsInstance<Hideable>().syncOrToggleAll()
|
||||
GitLabStatistics.logToggledComments(project)
|
||||
@@ -214,20 +231,23 @@ private class DiffEditorModel(
|
||||
private inner class MappedDiscussion(vm: GitLabMergeRequestDiffDiscussionViewModel)
|
||||
: GitLabMergeRequestEditorMappedComponentModel.Discussion<GitLabMergeRequestDiffDiscussionViewModel>(vm) {
|
||||
override val isVisible: StateFlow<Boolean> = vm.isVisible.combineState(hiddenState) { visible, hidden -> visible && !hidden }
|
||||
override val line: StateFlow<Int?> = vm.location.mapState { loc -> loc?.let { locationToLine(it) } }
|
||||
override val range: StateFlow<LineRange?> = vm.location.mapState { it?.toLineRange(locationToLine) }
|
||||
override val line: StateFlow<Int?> = range.mapState { it?.end }
|
||||
}
|
||||
|
||||
private inner class MappedDraftNote(vm: GitLabMergeRequestDiffDraftNoteViewModel)
|
||||
: GitLabMergeRequestEditorMappedComponentModel.DraftNote<GitLabMergeRequestDiffDraftNoteViewModel>(vm) {
|
||||
override val isVisible: StateFlow<Boolean> = vm.isVisible.combineState(hiddenState) { visible, hidden -> visible && !hidden }
|
||||
override val line: StateFlow<Int?> = vm.location.mapState { loc -> loc?.let { locationToLine(it) } }
|
||||
override val range: StateFlow<LineRange?> = vm.location.mapState { it?.toLineRange(locationToLine) }
|
||||
override val line: StateFlow<Int?> = range.mapState { it?.end }
|
||||
}
|
||||
|
||||
private inner class MappedNewDiscussion(vm: GitLabMergeRequestDiffNewDiscussionViewModel)
|
||||
: GitLabMergeRequestEditorMappedComponentModel.NewDiscussion<GitLabMergeRequestDiffNewDiscussionViewModel>(vm) {
|
||||
override val key: Any = "NEW_${vm.location.value}"
|
||||
override val isVisible: StateFlow<Boolean> = MutableStateFlow(true)
|
||||
override val line: StateFlow<Int?> = vm.location.mapState { loc -> loc?.let { locationToLine(it) } }
|
||||
override val range: StateFlow<LineRange?> = vm.location.mapState { it?.toLineRange(locationToLine) }
|
||||
override val line: StateFlow<Int?> = range.mapState { it?.end }
|
||||
|
||||
override fun cancel() {
|
||||
vm.location.value?.let(diffReviewVm::cancelNewDiscussion)
|
||||
@@ -240,4 +260,10 @@ private class DiffEditorModel(
|
||||
) : CodeReviewEditorGutterControlsModel.ControlsState {
|
||||
override fun isLineCommentable(lineIdx: Int): Boolean = true
|
||||
}
|
||||
}
|
||||
|
||||
private fun GitLabNoteLocation.toLineRange(locationToLine: (DiffLineLocation) -> Int?): LineRange? {
|
||||
val start = locationToLine(startSide to startLineIdx) ?: return null
|
||||
val end = locationToLine(side to lineIdx) ?: return null
|
||||
return LineRange(start, end)
|
||||
}
|
||||
+3
-1
@@ -178,7 +178,9 @@ internal class GitLabMergeRequestDiffProcessorViewModelImpl(
|
||||
|
||||
val change = mappedVm.diffData.value?.change ?: return
|
||||
|
||||
delegate.showChange(change, location?.let(DiffViewerScrollRequest::toLine))
|
||||
delegate.showChange(change, location?.let {
|
||||
DiffViewerScrollRequest.toLine(it.side to it.lineIdx)
|
||||
})
|
||||
mappedVm.requestFocus()
|
||||
}
|
||||
|
||||
|
||||
+7
-7
@@ -5,11 +5,11 @@ import com.intellij.collaboration.async.combineState
|
||||
import com.intellij.collaboration.async.combineStates
|
||||
import com.intellij.collaboration.async.mapState
|
||||
import com.intellij.collaboration.ui.FocusableViewModel
|
||||
import com.intellij.collaboration.ui.codereview.diff.DiffLineLocation
|
||||
import com.intellij.collaboration.ui.codereview.diff.DiscussionsViewOption
|
||||
import com.intellij.diff.util.Side
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import org.jetbrains.plugins.gitlab.mergerequest.data.GitLabNoteLocation
|
||||
import org.jetbrains.plugins.gitlab.mergerequest.data.GitLabNotePosition
|
||||
import org.jetbrains.plugins.gitlab.mergerequest.data.mapToLocation
|
||||
import org.jetbrains.plugins.gitlab.mergerequest.ui.DiffDataMappedGitLabMergeRequestInlayModel
|
||||
@@ -21,7 +21,7 @@ import org.jetbrains.plugins.gitlab.ui.comment.NewGitLabNoteViewModel
|
||||
interface DiffDataMappedGitLabMergeRequestDiffInlayViewModel
|
||||
: FocusableViewModel,
|
||||
DiffDataMappedGitLabMergeRequestInlayModel {
|
||||
val location: StateFlow<DiffLineLocation?>
|
||||
val location: StateFlow<GitLabNoteLocation?>
|
||||
val isVisible: StateFlow<Boolean>
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ class GitLabMergeRequestDiffDiscussionViewModel internal constructor(
|
||||
override val diffData: StateFlow<DiffDataMappedGitLabMergeRequestInlayModel.DiffData?>,
|
||||
discussionsViewOption: StateFlow<DiscussionsViewOption>,
|
||||
) : GitLabMergeRequestDiscussionViewModel by base, DiffDataMappedGitLabMergeRequestDiffInlayViewModel {
|
||||
override val location: StateFlow<DiffLineLocation?> = mapPositionToDiffLine(base.position, diffData)
|
||||
override val location: StateFlow<GitLabNoteLocation?> = mapPositionToDiffLine(base.position, diffData)
|
||||
|
||||
override val isVisible: StateFlow<Boolean> = isResolved.combineState(discussionsViewOption) { isResolved, viewOption ->
|
||||
when (viewOption) {
|
||||
@@ -46,7 +46,7 @@ class GitLabMergeRequestDiffDraftNoteViewModel internal constructor(
|
||||
override val diffData: StateFlow<DiffDataMappedGitLabMergeRequestInlayModel.DiffData?>,
|
||||
discussionsViewOption: StateFlow<DiscussionsViewOption>,
|
||||
) : GitLabNoteViewModel by base, DiffDataMappedGitLabMergeRequestDiffInlayViewModel {
|
||||
override val location: StateFlow<DiffLineLocation?> = mapPositionToDiffLine(base.position, diffData)
|
||||
override val location: StateFlow<GitLabNoteLocation?> = mapPositionToDiffLine(base.position, diffData)
|
||||
|
||||
override val isVisible: StateFlow<Boolean> = discussionsViewOption.mapState {
|
||||
when (it) {
|
||||
@@ -58,17 +58,17 @@ class GitLabMergeRequestDiffDraftNoteViewModel internal constructor(
|
||||
|
||||
class GitLabMergeRequestDiffNewDiscussionViewModel internal constructor(
|
||||
base: NewGitLabNoteViewModel,
|
||||
originalLocation: DiffLineLocation,
|
||||
originalLocation: GitLabNoteLocation,
|
||||
discussionsViewOption: StateFlow<DiscussionsViewOption>,
|
||||
) : NewGitLabNoteViewModel by base {
|
||||
val location: StateFlow<DiffLineLocation?> = MutableStateFlow(originalLocation)
|
||||
val location: StateFlow<GitLabNoteLocation?> = MutableStateFlow(originalLocation)
|
||||
val isVisible: StateFlow<Boolean> = discussionsViewOption.mapState { it != DiscussionsViewOption.DONT_SHOW }
|
||||
}
|
||||
|
||||
private fun mapPositionToDiffLine(
|
||||
position: StateFlow<GitLabNotePosition?>,
|
||||
diffData: StateFlow<DiffDataMappedGitLabMergeRequestInlayModel.DiffData?>,
|
||||
): StateFlow<DiffLineLocation?> =
|
||||
): StateFlow<GitLabNoteLocation?> =
|
||||
combineStates(position, diffData) { positionOrNull, diffDataOrNull ->
|
||||
val position = positionOrNull ?: return@combineStates null
|
||||
val diffData = diffDataOrNull?.diffData ?: return@combineStates null
|
||||
|
||||
+15
-4
@@ -23,6 +23,7 @@ import org.jetbrains.plugins.gitlab.api.dto.GitLabUserDTO
|
||||
import org.jetbrains.plugins.gitlab.data.GitLabImageLoader
|
||||
import org.jetbrains.plugins.gitlab.mergerequest.data.GitLabMergeRequest
|
||||
import org.jetbrains.plugins.gitlab.mergerequest.data.GitLabMergeRequestNewDiscussionPosition
|
||||
import org.jetbrains.plugins.gitlab.mergerequest.data.GitLabNoteLocation
|
||||
import org.jetbrains.plugins.gitlab.mergerequest.data.mapToLocation
|
||||
import org.jetbrains.plugins.gitlab.mergerequest.diff.GitLabMergeRequestDiffViewModel
|
||||
import org.jetbrains.plugins.gitlab.mergerequest.ui.details.model.GitLabPersistentMergeRequestChangesViewedState
|
||||
@@ -39,8 +40,8 @@ interface GitLabMergeRequestDiffReviewViewModel {
|
||||
val draftDiscussions: StateFlow<ComputedResult<Collection<GitLabMergeRequestDiffDraftNoteViewModel>>>
|
||||
val newDiscussions: StateFlow<Collection<GitLabMergeRequestDiffNewDiscussionViewModel>>
|
||||
|
||||
val locationsWithDiscussions: StateFlow<Set<DiffLineLocation>>
|
||||
val locationsWithNewDiscussions: StateFlow<Set<DiffLineLocation>>
|
||||
val locationsWithDiscussions: StateFlow<Set<GitLabNoteLocation>>
|
||||
val locationsWithNewDiscussions: StateFlow<Set<GitLabNoteLocation>>
|
||||
|
||||
val avatarIconsProvider: IconsProvider<GitLabUserDTO>
|
||||
val imageLoader: GitLabImageLoader
|
||||
@@ -54,6 +55,8 @@ interface GitLabMergeRequestDiffReviewViewModel {
|
||||
|
||||
fun requestNewDiscussion(location: DiffLineLocation, focus: Boolean)
|
||||
fun cancelNewDiscussion(location: DiffLineLocation)
|
||||
fun requestNewDiscussion(location: GitLabNoteLocation, focus: Boolean)
|
||||
fun cancelNewDiscussion(location: GitLabNoteLocation)
|
||||
|
||||
fun markViewed()
|
||||
}
|
||||
@@ -91,13 +94,13 @@ internal class GitLabMergeRequestDiffReviewViewModelImpl(
|
||||
}
|
||||
}.stateInNow(cs, emptyList())
|
||||
|
||||
override val locationsWithDiscussions: StateFlow<Set<DiffLineLocation>> = GitLabMergeRequestDiscussionUtil
|
||||
override val locationsWithDiscussions: StateFlow<Set<GitLabNoteLocation>> = GitLabMergeRequestDiscussionUtil
|
||||
.createDiscussionsPositionsFlow(mergeRequest, discussionsViewOption).toLocations {
|
||||
it.mapToLocation(diffData, Side.LEFT)
|
||||
}.stateInNow(cs, emptySet())
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
override val locationsWithNewDiscussions: StateFlow<Set<DiffLineLocation>> =
|
||||
override val locationsWithNewDiscussions: StateFlow<Set<GitLabNoteLocation>> =
|
||||
discussionsContainer.newDiscussions
|
||||
.map {
|
||||
it.keys.mapNotNullTo(mutableSetOf()) {
|
||||
@@ -120,6 +123,14 @@ internal class GitLabMergeRequestDiffReviewViewModelImpl(
|
||||
discussionsContainer.cancelNewDiscussion(position)
|
||||
}
|
||||
|
||||
override fun requestNewDiscussion(location: GitLabNoteLocation, focus: Boolean) {
|
||||
TODO("not implemented")
|
||||
}
|
||||
|
||||
override fun cancelNewDiscussion(location: GitLabNoteLocation) {
|
||||
TODO("not implemented")
|
||||
}
|
||||
|
||||
override fun markViewed() {
|
||||
val sha = mergeRequest.details.value.diffRefs?.headSha ?: return
|
||||
persistentChangesViewedState.markViewed(
|
||||
|
||||
+19
-12
@@ -11,6 +11,7 @@ import com.intellij.diff.util.Side
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import org.jetbrains.annotations.ApiStatus
|
||||
import org.jetbrains.plugins.gitlab.mergerequest.data.GitLabNoteLocation
|
||||
import org.jetbrains.plugins.gitlab.mergerequest.data.GitLabNotePosition
|
||||
import org.jetbrains.plugins.gitlab.mergerequest.data.mapToLocation
|
||||
import org.jetbrains.plugins.gitlab.mergerequest.ui.DiffDataMappedGitLabMergeRequestInlayModel
|
||||
@@ -18,12 +19,15 @@ import org.jetbrains.plugins.gitlab.ui.comment.GitLabMergeRequestDiscussionViewM
|
||||
import org.jetbrains.plugins.gitlab.ui.comment.GitLabMergeRequestStandaloneDraftNoteViewModelBase
|
||||
import org.jetbrains.plugins.gitlab.ui.comment.GitLabNoteViewModel
|
||||
import org.jetbrains.plugins.gitlab.ui.comment.NewGitLabNoteViewModel
|
||||
import java.util.*
|
||||
|
||||
@ApiStatus.Internal
|
||||
interface DiffDataMappedGitLabMergeRequestEditorViewModel
|
||||
: CodeReviewInlayModel,
|
||||
DiffDataMappedGitLabMergeRequestInlayModel,
|
||||
FocusableViewModel
|
||||
FocusableViewModel {
|
||||
val location: StateFlow<GitLabNoteLocation?>
|
||||
}
|
||||
|
||||
@ApiStatus.Internal
|
||||
class GitLabMergeRequestEditorDiscussionViewModel(
|
||||
@@ -32,7 +36,8 @@ class GitLabMergeRequestEditorDiscussionViewModel(
|
||||
discussionsViewOption: StateFlow<DiscussionsViewOption>,
|
||||
) : GitLabMergeRequestDiscussionViewModel by base, DiffDataMappedGitLabMergeRequestEditorViewModel {
|
||||
override val key: Any = base.id
|
||||
override val line: StateFlow<Int?> = mapPositionToRightLine(base.position, diffData)
|
||||
override val location: StateFlow<GitLabNoteLocation?> = mapPositionToRightLocation(base.position, diffData)
|
||||
override val line: StateFlow<Int?> = location.mapState { it?.lineIdx }
|
||||
|
||||
override val isVisible: StateFlow<Boolean> = isResolved.combineState(discussionsViewOption) { isResolved, viewOption ->
|
||||
return@combineState when (viewOption) {
|
||||
@@ -50,7 +55,8 @@ class GitLabMergeRequestEditorDraftNoteViewModel internal constructor(
|
||||
discussionsViewOption: StateFlow<DiscussionsViewOption>,
|
||||
) : GitLabNoteViewModel by base, DiffDataMappedGitLabMergeRequestEditorViewModel {
|
||||
override val key: Any = base.id
|
||||
override val line: StateFlow<Int?> = mapPositionToRightLine(base.position, diffData)
|
||||
override val location: StateFlow<GitLabNoteLocation?> = mapPositionToRightLocation(base.position, diffData)
|
||||
override val line: StateFlow<Int?> = location.mapState { it?.lineIdx }
|
||||
|
||||
override val isVisible: StateFlow<Boolean> = discussionsViewOption.mapState {
|
||||
when (it) {
|
||||
@@ -63,33 +69,34 @@ class GitLabMergeRequestEditorDraftNoteViewModel internal constructor(
|
||||
@ApiStatus.Internal
|
||||
class GitLabMergeRequestEditorNewDiscussionViewModel internal constructor(
|
||||
base: NewGitLabNoteViewModel,
|
||||
originalLine: Int,
|
||||
originalLocation: GitLabNoteLocation,
|
||||
discussionsViewOption: StateFlow<DiscussionsViewOption>,
|
||||
) : NewGitLabNoteViewModel by base, CodeReviewInlayModel {
|
||||
override val key: Any = "NEW_${originalLine}"
|
||||
override val line: StateFlow<Int?> = MutableStateFlow(originalLine)
|
||||
override val key: Any = "NEW_${UUID.randomUUID()}"
|
||||
val location: StateFlow<GitLabNoteLocation?> = MutableStateFlow(originalLocation)
|
||||
override val line: StateFlow<Int?> = location.mapState { it?.lineIdx }
|
||||
override val isVisible: StateFlow<Boolean> = discussionsViewOption.mapState { it != DiscussionsViewOption.DONT_SHOW }
|
||||
}
|
||||
|
||||
private fun mapPositionToRightLine(
|
||||
private fun mapPositionToRightLocation(
|
||||
position: GitLabNotePosition?,
|
||||
diffData: StateFlow<DiffDataMappedGitLabMergeRequestInlayModel.DiffData?>,
|
||||
): StateFlow<Int?> =
|
||||
): StateFlow<GitLabNoteLocation?> =
|
||||
diffData.mapState { diffDataOrNull ->
|
||||
val diffData = diffDataOrNull?.diffData ?: return@mapState null
|
||||
|
||||
position?.mapToLocation(diffData, Side.RIGHT)
|
||||
?.takeIf { it.first == Side.RIGHT }?.second
|
||||
?.takeIf { it.startSide == Side.RIGHT && it.side == Side.RIGHT }
|
||||
}
|
||||
|
||||
private fun mapPositionToRightLine(
|
||||
private fun mapPositionToRightLocation(
|
||||
position: StateFlow<GitLabNotePosition?>,
|
||||
diffData: StateFlow<DiffDataMappedGitLabMergeRequestInlayModel.DiffData?>,
|
||||
): StateFlow<Int?> =
|
||||
): StateFlow<GitLabNoteLocation?> =
|
||||
combineStates(position, diffData) { positionOrNull, diffDataOrNull ->
|
||||
val position = positionOrNull ?: return@combineStates null
|
||||
val diffData = diffDataOrNull?.diffData ?: return@combineStates null
|
||||
|
||||
position.mapToLocation(diffData, Side.RIGHT)
|
||||
?.takeIf { it.first == Side.RIGHT }?.second
|
||||
?.takeIf { it.startSide == Side.RIGHT && it.side == Side.RIGHT }
|
||||
}
|
||||
|
||||
+18
-7
@@ -4,9 +4,11 @@ package org.jetbrains.plugins.gitlab.mergerequest.ui.editor
|
||||
import com.intellij.collaboration.async.collectScoped
|
||||
import com.intellij.collaboration.async.launchNow
|
||||
import com.intellij.collaboration.ui.codereview.diff.DiscussionsViewOption
|
||||
import com.intellij.collaboration.ui.codereview.editor.CodeReviewActiveRangesTracker
|
||||
import com.intellij.collaboration.ui.codereview.editor.CodeReviewCommentableEditorModel
|
||||
import com.intellij.collaboration.ui.codereview.editor.CodeReviewEditorGutterChangesRenderer
|
||||
import com.intellij.collaboration.ui.codereview.editor.CodeReviewEditorGutterControlsRenderer
|
||||
import com.intellij.collaboration.ui.codereview.editor.CodeReviewEditorInlayRangeOutlineUtils
|
||||
import com.intellij.collaboration.ui.codereview.editor.CodeReviewNavigableEditorViewModel
|
||||
import com.intellij.collaboration.ui.codereview.editor.ReviewInEditorUtil
|
||||
import com.intellij.collaboration.ui.codereview.editor.renderInlays
|
||||
@@ -40,6 +42,7 @@ import kotlinx.coroutines.flow.flatMapLatest
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.mapNotNull
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.supervisorScope
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.jetbrains.plugins.gitlab.api.dto.GitLabUserDTO
|
||||
@@ -146,8 +149,15 @@ internal class GitLabMergeRequestEditorReviewController(private val project: Pro
|
||||
CodeReviewEditorGutterControlsRenderer.render(model, editor)
|
||||
}
|
||||
launchNow {
|
||||
editor.renderInlays(model.inlays, HashingUtil.mappingStrategy(GitLabMergeRequestEditorMappedComponentModel::key)) {
|
||||
createRenderer(it, fileVm.avatarIconsProvider, fileVm.imageLoader)
|
||||
val activeRangesTracker = CodeReviewActiveRangesTracker()
|
||||
editor.renderInlays(
|
||||
model.inlays,
|
||||
HashingUtil.mappingStrategy(GitLabMergeRequestEditorMappedComponentModel::key)) { inlayModel ->
|
||||
createRenderer(inlayModel, fileVm.avatarIconsProvider, fileVm.imageLoader, activeRangesTracker).also { inlayRenderer ->
|
||||
launch {
|
||||
CodeReviewEditorInlayRangeOutlineUtils.showInlayOutline(editor, model, inlayModel, inlayRenderer, activeRangesTracker)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -167,18 +177,19 @@ internal class GitLabMergeRequestEditorReviewController(private val project: Pro
|
||||
inlayModel: GitLabMergeRequestEditorMappedComponentModel,
|
||||
avatarIconsProvider: IconsProvider<GitLabUserDTO>,
|
||||
imageLoader: GitLabImageLoader,
|
||||
activeRangesTracker: CodeReviewActiveRangesTracker,
|
||||
) =
|
||||
when (inlayModel) {
|
||||
is GitLabMergeRequestEditorMappedComponentModel.Discussion<*> ->
|
||||
GitLabMergeRequestDiscussionInlayRenderer(this, project, inlayModel.vm, avatarIconsProvider,
|
||||
imageLoader,
|
||||
GitLabMergeRequestDiscussionInlayRenderer(this, project, inlayModel, avatarIconsProvider,
|
||||
imageLoader, activeRangesTracker,
|
||||
GitLabStatistics.MergeRequestNoteActionPlace.EDITOR)
|
||||
is GitLabMergeRequestEditorMappedComponentModel.DraftNote<*> ->
|
||||
GitLabMergeRequestDraftNoteInlayRenderer(this, project, inlayModel.vm, avatarIconsProvider,
|
||||
imageLoader,
|
||||
GitLabMergeRequestDraftNoteInlayRenderer(this, project, inlayModel, avatarIconsProvider,
|
||||
imageLoader, activeRangesTracker,
|
||||
GitLabStatistics.MergeRequestNoteActionPlace.EDITOR)
|
||||
is GitLabMergeRequestEditorMappedComponentModel.NewDiscussion<*> ->
|
||||
GitLabMergeRequestNewDiscussionInlayRenderer(this, project, inlayModel.vm, avatarIconsProvider,
|
||||
GitLabMergeRequestNewDiscussionInlayRenderer(this, project, inlayModel, avatarIconsProvider, activeRangesTracker,
|
||||
GitLabStatistics.MergeRequestNoteActionPlace.EDITOR, inlayModel::cancel)
|
||||
|
||||
}
|
||||
|
||||
+9
-4
@@ -124,15 +124,18 @@ internal class GitLabMergeRequestEditorReviewFileViewModelImpl(
|
||||
override val newDiscussions: StateFlow<Collection<GitLabMergeRequestEditorNewDiscussionViewModel>> =
|
||||
discussionsContainer.newDiscussions.map {
|
||||
it.mapNotNull { (position, vm) ->
|
||||
val line = position.mapToLocation(diffData)?.takeIf { it.first == Side.RIGHT }?.second ?: return@mapNotNull null
|
||||
GitLabMergeRequestEditorNewDiscussionViewModel(vm, line, discussionsViewOption)
|
||||
val location =
|
||||
position.mapToLocation(diffData)?.takeIf { it.startSide == Side.RIGHT && it.side == Side.RIGHT } ?: return@mapNotNull null
|
||||
GitLabMergeRequestEditorNewDiscussionViewModel(vm, location, discussionsViewOption)
|
||||
}
|
||||
}.stateInNow(cs, emptyList())
|
||||
|
||||
override val linesWithDiscussions: StateFlow<Set<Int>> =
|
||||
GitLabMergeRequestDiscussionUtil
|
||||
.createDiscussionsPositionsFlow(mergeRequest, discussionsViewOption).toLines {
|
||||
it.mapToLocation(diffData, Side.RIGHT)?.takeIf { it.first == Side.RIGHT }?.second
|
||||
it.mapToLocation(diffData, Side.RIGHT)?.takeIf {
|
||||
it.startSide == Side.RIGHT && it.side == Side.RIGHT
|
||||
}?.lineIdx
|
||||
}.stateInNow(cs, emptySet())
|
||||
|
||||
override val canNavigate: Boolean = diffData.isCumulative
|
||||
@@ -144,7 +147,9 @@ internal class GitLabMergeRequestEditorReviewFileViewModelImpl(
|
||||
discussionsContainer.newDiscussions
|
||||
.map {
|
||||
it.keys.mapNotNullTo(mutableSetOf()) {
|
||||
it.mapToLocation(diffData)?.takeIf { it.first == Side.RIGHT }?.second ?: return@mapNotNullTo null
|
||||
it.mapToLocation(diffData)?.takeIf {
|
||||
it.startSide == Side.RIGHT && it.side == Side.RIGHT
|
||||
}?.lineIdx ?: return@mapNotNullTo null
|
||||
}
|
||||
}
|
||||
.stateInNow(cs, emptySet())
|
||||
|
||||
+24
-10
@@ -3,9 +3,11 @@ package org.jetbrains.plugins.gitlab.mergerequest.ui.editor
|
||||
|
||||
import com.intellij.collaboration.async.combineState
|
||||
import com.intellij.collaboration.async.launchNow
|
||||
import com.intellij.collaboration.async.mapState
|
||||
import com.intellij.collaboration.async.mapStatefulToStateful
|
||||
import com.intellij.collaboration.async.stateInNow
|
||||
import com.intellij.collaboration.async.transformConsecutiveSuccesses
|
||||
import com.intellij.collaboration.ui.codereview.editor.CodeReviewCommentableEditorModel
|
||||
import com.intellij.collaboration.ui.codereview.editor.CodeReviewEditorGutterActionableChangesModel
|
||||
import com.intellij.collaboration.ui.codereview.editor.CodeReviewEditorGutterChangesModel
|
||||
import com.intellij.collaboration.ui.codereview.editor.CodeReviewEditorGutterControlsModel
|
||||
@@ -32,6 +34,7 @@ import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import org.jetbrains.plugins.gitlab.mergerequest.GitLabMergeRequestsPreferences
|
||||
import org.jetbrains.plugins.gitlab.mergerequest.data.GitLabNoteLocation
|
||||
import org.jetbrains.plugins.gitlab.util.GitLabStatistics
|
||||
|
||||
/**
|
||||
@@ -48,6 +51,7 @@ internal class GitLabMergeRequestEditorReviewUIModel internal constructor(
|
||||
CodeReviewEditorGutterActionableChangesModel,
|
||||
CodeReviewEditorInlaysModel<GitLabMergeRequestEditorMappedComponentModel>,
|
||||
CodeReviewEditorGutterControlsModel,
|
||||
CodeReviewCommentableEditorModel.WithMultilineComments,
|
||||
CodeReviewNavigableEditorViewModel {
|
||||
|
||||
private val postReviewRanges = MutableStateFlow<List<Range>?>(null)
|
||||
@@ -105,6 +109,14 @@ internal class GitLabMergeRequestEditorReviewUIModel internal constructor(
|
||||
fileVm.cancelNewDiscussion(originalLine)
|
||||
}
|
||||
|
||||
override fun requestNewComment(lineRange: LineRange) {
|
||||
TODO("not implemented")
|
||||
}
|
||||
|
||||
override fun canCreateComment(lineRange: LineRange): Boolean {
|
||||
TODO("not implemented")
|
||||
}
|
||||
|
||||
override fun toggleComments(lineIdx: Int) {
|
||||
inlays.value.asSequence().filter { it.line.value == lineIdx }.filterIsInstance<Hideable>().syncOrToggleAll()
|
||||
GitLabStatistics.logToggledComments(project)
|
||||
@@ -184,12 +196,12 @@ internal class GitLabMergeRequestEditorReviewUIModel internal constructor(
|
||||
return inlay.isVisible.value
|
||||
}
|
||||
|
||||
private fun StateFlow<Int?>.shiftLine(): StateFlow<Int?> =
|
||||
combineState(postReviewRanges) { line, ranges ->
|
||||
if (ranges != null && line != null) {
|
||||
ReviewInEditorUtil.transferLineToAfter(ranges, line).takeIf { it >= 0 }
|
||||
}
|
||||
else null
|
||||
private fun StateFlow<GitLabNoteLocation?>.shiftLineRange(): StateFlow<LineRange?> =
|
||||
combineState(postReviewRanges) { vmsLocation, ranges ->
|
||||
if (ranges == null || vmsLocation == null) return@combineState null
|
||||
val start = ReviewInEditorUtil.transferLineToAfter(ranges, vmsLocation.startLineIdx)
|
||||
val end = ReviewInEditorUtil.transferLineToAfter(ranges, vmsLocation.lineIdx)
|
||||
LineRange(start, end)
|
||||
}
|
||||
|
||||
private fun Int.shiftLineToAfter(): Int? {
|
||||
@@ -205,21 +217,23 @@ internal class GitLabMergeRequestEditorReviewUIModel internal constructor(
|
||||
private inner class ShiftedDiscussion(vm: GitLabMergeRequestEditorDiscussionViewModel)
|
||||
: GitLabMergeRequestEditorMappedComponentModel.Discussion<GitLabMergeRequestEditorDiscussionViewModel>(vm) {
|
||||
override val isVisible: StateFlow<Boolean> = vm.isVisible.combineState(hiddenState) { visible, hidden -> visible && !hidden }
|
||||
override val line: StateFlow<Int?> = vm.line.shiftLine()
|
||||
override val range: StateFlow<LineRange?> = vm.location.shiftLineRange()
|
||||
override val line: StateFlow<Int?> = range.mapState { it?.end }
|
||||
}
|
||||
|
||||
private inner class ShiftedDraftNote(vm: GitLabMergeRequestEditorDraftNoteViewModel)
|
||||
: GitLabMergeRequestEditorMappedComponentModel.DraftNote<GitLabMergeRequestEditorDraftNoteViewModel>(vm) {
|
||||
override val isVisible: StateFlow<Boolean> = vm.isVisible.combineState(hiddenState) { visible, hidden -> visible && !hidden }
|
||||
override val line: StateFlow<Int?> = vm.line.shiftLine()
|
||||
override val range: StateFlow<LineRange?> = vm.location.shiftLineRange()
|
||||
override val line: StateFlow<Int?> = range.mapState { it?.end }
|
||||
}
|
||||
|
||||
private inner class ShiftedNewDiscussion(vm: GitLabMergeRequestEditorNewDiscussionViewModel)
|
||||
: GitLabMergeRequestEditorMappedComponentModel.NewDiscussion<GitLabMergeRequestEditorNewDiscussionViewModel>(vm) {
|
||||
override val key: Any = vm.key
|
||||
override val isVisible: StateFlow<Boolean> = MutableStateFlow(true)
|
||||
override val line: StateFlow<Int?> = vm.line.shiftLine()
|
||||
|
||||
override val range: StateFlow<LineRange?> = vm.location.shiftLineRange()
|
||||
override val line: StateFlow<Int?> = range.mapState { it?.end }
|
||||
override fun cancel() {
|
||||
vm.line.value?.let(::cancelNewDiscussion)
|
||||
}
|
||||
|
||||
+35
-24
@@ -1,7 +1,9 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package org.jetbrains.plugins.gitlab.mergerequest.ui.editor
|
||||
|
||||
import com.intellij.collaboration.ui.codereview.editor.CodeReviewActiveRangesTracker
|
||||
import com.intellij.collaboration.ui.codereview.editor.CodeReviewComponentInlayRenderer
|
||||
import com.intellij.collaboration.ui.codereview.editor.CodeReviewEditorInlayRangeOutlineUtils
|
||||
import com.intellij.collaboration.ui.icon.IconsProvider
|
||||
import com.intellij.openapi.project.Project
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
@@ -9,43 +11,52 @@ import org.jetbrains.annotations.ApiStatus
|
||||
import org.jetbrains.plugins.gitlab.api.dto.GitLabUserDTO
|
||||
import org.jetbrains.plugins.gitlab.data.GitLabImageLoader
|
||||
import org.jetbrains.plugins.gitlab.mergerequest.ui.diff.GitLabMergeRequestDiffInlayComponentsFactory
|
||||
import org.jetbrains.plugins.gitlab.ui.comment.GitLabMergeRequestDiscussionViewModel
|
||||
import org.jetbrains.plugins.gitlab.ui.comment.GitLabNoteViewModel
|
||||
import org.jetbrains.plugins.gitlab.ui.comment.NewGitLabNoteViewModel
|
||||
import org.jetbrains.plugins.gitlab.util.GitLabStatistics
|
||||
|
||||
@ApiStatus.Internal
|
||||
class GitLabMergeRequestDiscussionInlayRenderer internal constructor(
|
||||
cs: CoroutineScope,
|
||||
project: Project,
|
||||
vm: GitLabMergeRequestDiscussionViewModel,
|
||||
model: GitLabMergeRequestEditorMappedComponentModel.Discussion<*>,
|
||||
avatarIconsProvider: IconsProvider<GitLabUserDTO>,
|
||||
imageLoader: GitLabImageLoader,
|
||||
activeRangesTracker: CodeReviewActiveRangesTracker,
|
||||
place: GitLabStatistics.MergeRequestNoteActionPlace,
|
||||
)
|
||||
: CodeReviewComponentInlayRenderer(
|
||||
GitLabMergeRequestDiffInlayComponentsFactory.createDiscussion(project, cs, avatarIconsProvider, imageLoader, vm, place)
|
||||
) : CodeReviewComponentInlayRenderer(
|
||||
GitLabMergeRequestDiffInlayComponentsFactory.createDiscussion(project, cs, avatarIconsProvider, imageLoader, model.vm, place)
|
||||
.let { newCommentComponent ->
|
||||
CodeReviewEditorInlayRangeOutlineUtils.wrapWithDimming(newCommentComponent, model, activeRangesTracker)
|
||||
}
|
||||
)
|
||||
|
||||
@ApiStatus.Internal
|
||||
class GitLabMergeRequestDraftNoteInlayRenderer internal constructor(cs: CoroutineScope,
|
||||
project: Project,
|
||||
vm: GitLabNoteViewModel,
|
||||
avatarIconsProvider: IconsProvider<GitLabUserDTO>,
|
||||
imageLoader: GitLabImageLoader,
|
||||
place: GitLabStatistics.MergeRequestNoteActionPlace,
|
||||
)
|
||||
: CodeReviewComponentInlayRenderer(
|
||||
GitLabMergeRequestDiffInlayComponentsFactory.createDraftNote(project, cs, avatarIconsProvider, imageLoader, vm, place)
|
||||
class GitLabMergeRequestDraftNoteInlayRenderer internal constructor(
|
||||
cs: CoroutineScope,
|
||||
project: Project,
|
||||
model: GitLabMergeRequestEditorMappedComponentModel.DraftNote<*>,
|
||||
avatarIconsProvider: IconsProvider<GitLabUserDTO>,
|
||||
imageLoader: GitLabImageLoader,
|
||||
activeRangesTracker: CodeReviewActiveRangesTracker,
|
||||
place: GitLabStatistics.MergeRequestNoteActionPlace,
|
||||
) : CodeReviewComponentInlayRenderer(
|
||||
GitLabMergeRequestDiffInlayComponentsFactory.createDraftNote(project, cs, avatarIconsProvider, imageLoader, model.vm, place)
|
||||
.let { newCommentComponent ->
|
||||
CodeReviewEditorInlayRangeOutlineUtils.wrapWithDimming(newCommentComponent, model, activeRangesTracker)
|
||||
}
|
||||
)
|
||||
|
||||
@ApiStatus.Internal
|
||||
class GitLabMergeRequestNewDiscussionInlayRenderer internal constructor(cs: CoroutineScope,
|
||||
project: Project,
|
||||
vm: NewGitLabNoteViewModel,
|
||||
avatarIconsProvider: IconsProvider<GitLabUserDTO>,
|
||||
place: GitLabStatistics.MergeRequestNoteActionPlace,
|
||||
onCancel: () -> Unit)
|
||||
: CodeReviewComponentInlayRenderer(
|
||||
GitLabMergeRequestDiffInlayComponentsFactory.createNewDiscussion(project, cs, avatarIconsProvider, vm, onCancel, place)
|
||||
class GitLabMergeRequestNewDiscussionInlayRenderer internal constructor(
|
||||
cs: CoroutineScope,
|
||||
project: Project,
|
||||
model: GitLabMergeRequestEditorMappedComponentModel.NewDiscussion<*>,
|
||||
avatarIconsProvider: IconsProvider<GitLabUserDTO>,
|
||||
activeRangesTracker: CodeReviewActiveRangesTracker,
|
||||
place: GitLabStatistics.MergeRequestNoteActionPlace,
|
||||
onCancel: () -> Unit,
|
||||
) : CodeReviewComponentInlayRenderer(
|
||||
GitLabMergeRequestDiffInlayComponentsFactory.createNewDiscussion(project, cs, avatarIconsProvider, model.vm, onCancel, place)
|
||||
.let { newCommentComponent ->
|
||||
CodeReviewEditorInlayRangeOutlineUtils.wrapWithDimming(newCommentComponent, model, activeRangesTracker)
|
||||
}
|
||||
)
|
||||
+1
-1
@@ -9,7 +9,7 @@ import org.jetbrains.plugins.gitlab.ui.comment.GitLabMergeRequestDiscussionViewM
|
||||
import org.jetbrains.plugins.gitlab.ui.comment.GitLabNoteViewModel
|
||||
import org.jetbrains.plugins.gitlab.ui.comment.NewGitLabNoteViewModel
|
||||
|
||||
internal sealed interface GitLabMergeRequestEditorMappedComponentModel : CodeReviewInlayModel {
|
||||
internal sealed interface GitLabMergeRequestEditorMappedComponentModel : CodeReviewInlayModel.Ranged {
|
||||
val vm: CodeReviewTrackableItemViewModel
|
||||
|
||||
abstract class Discussion<VM : GitLabMergeRequestDiscussionViewModel>(override val vm: VM)
|
||||
|
||||
+5
-32
@@ -1,12 +1,7 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package org.jetbrains.plugins.gitlab.mergerequest.ui.review
|
||||
|
||||
import com.intellij.collaboration.async.cancelAndJoinSilently
|
||||
import com.intellij.collaboration.async.mapFiltered
|
||||
import com.intellij.collaboration.async.mapState
|
||||
import com.intellij.collaboration.async.mapStatefulToStateful
|
||||
import com.intellij.collaboration.async.stateInNow
|
||||
import com.intellij.collaboration.async.transformConsecutiveSuccesses
|
||||
import com.intellij.collaboration.async.*
|
||||
import com.intellij.collaboration.ui.codereview.diff.DiffLineLocation
|
||||
import com.intellij.collaboration.ui.codereview.diff.UnifiedCodeReviewItemPosition
|
||||
import com.intellij.collaboration.util.ComputedResult
|
||||
@@ -22,34 +17,12 @@ import git4idea.changes.findCumulativeChange
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.filterNotNull
|
||||
import kotlinx.coroutines.flow.flatMapLatest
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.update
|
||||
import kotlinx.coroutines.flow.updateAndGet
|
||||
import kotlinx.coroutines.flow.*
|
||||
import kotlinx.coroutines.launch
|
||||
import org.jetbrains.plugins.gitlab.api.dto.GitLabUserDTO
|
||||
import org.jetbrains.plugins.gitlab.mergerequest.data.GitLabMergeRequest
|
||||
import org.jetbrains.plugins.gitlab.mergerequest.data.GitLabMergeRequestNewDiscussionPosition
|
||||
import org.jetbrains.plugins.gitlab.mergerequest.data.GitLabNotePosition
|
||||
import org.jetbrains.plugins.gitlab.mergerequest.data.GitLabProject
|
||||
import org.jetbrains.plugins.gitlab.mergerequest.data.filePath
|
||||
import org.jetbrains.plugins.gitlab.mergerequest.data.mapToLeftSideLine
|
||||
import org.jetbrains.plugins.gitlab.mergerequest.data.mapToLocation
|
||||
import org.jetbrains.plugins.gitlab.mergerequest.data.mapToRightSideLine
|
||||
import org.jetbrains.plugins.gitlab.mergerequest.data.*
|
||||
import org.jetbrains.plugins.gitlab.ui.GitLabMarkdownToHtmlConverter
|
||||
import org.jetbrains.plugins.gitlab.ui.comment.GitLabMergeRequestDiscussionViewModel
|
||||
import org.jetbrains.plugins.gitlab.ui.comment.GitLabMergeRequestDiscussionViewModelBase
|
||||
import org.jetbrains.plugins.gitlab.ui.comment.GitLabMergeRequestStandaloneDraftNoteViewModelBase
|
||||
import org.jetbrains.plugins.gitlab.ui.comment.GitLabNoteEditingViewModel
|
||||
import org.jetbrains.plugins.gitlab.ui.comment.NewGitLabNoteViewModel
|
||||
import org.jetbrains.plugins.gitlab.ui.comment.onDoneIn
|
||||
import org.jetbrains.plugins.gitlab.ui.comment.*
|
||||
import java.time.Instant.EPOCH
|
||||
import java.util.Date
|
||||
import java.util.TreeSet
|
||||
@@ -94,7 +67,7 @@ interface GitLabMergeRequestDiscussionsViewModels {
|
||||
}
|
||||
}
|
||||
|
||||
fun GitLabMergeRequestDiscussionsViewModels.NewDiscussionPosition.mapToLocation(diffData: GitTextFilePatchWithHistory): DiffLineLocation? =
|
||||
fun GitLabMergeRequestDiscussionsViewModels.NewDiscussionPosition.mapToLocation(diffData: GitTextFilePatchWithHistory): GitLabNoteLocation? =
|
||||
position.mapToLocation(diffData, side)
|
||||
|
||||
internal class GitLabMergeRequestDiscussionsViewModelsImpl(
|
||||
|
||||
+2
-2
@@ -2,7 +2,6 @@
|
||||
package org.jetbrains.plugins.gitlab.mergerequest.util
|
||||
|
||||
import com.intellij.collaboration.async.flatMapLatestEach
|
||||
import com.intellij.collaboration.ui.codereview.diff.DiffLineLocation
|
||||
import com.intellij.collaboration.ui.codereview.diff.DiscussionsViewOption
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.combine
|
||||
@@ -11,6 +10,7 @@ import org.jetbrains.plugins.gitlab.mergerequest.data.GitLabMergeRequest
|
||||
import org.jetbrains.plugins.gitlab.mergerequest.data.GitLabMergeRequestDiscussion
|
||||
import org.jetbrains.plugins.gitlab.mergerequest.data.GitLabMergeRequestDraftNote
|
||||
import org.jetbrains.plugins.gitlab.mergerequest.data.GitLabMergeRequestNote
|
||||
import org.jetbrains.plugins.gitlab.mergerequest.data.GitLabNoteLocation
|
||||
import org.jetbrains.plugins.gitlab.mergerequest.data.GitLabNotePosition
|
||||
|
||||
internal object GitLabMergeRequestDiscussionUtil {
|
||||
@@ -67,5 +67,5 @@ internal object GitLabMergeRequestDiscussionUtil {
|
||||
fun Flow<Set<GitLabNotePosition>>.toLines(mapper: (GitLabNotePosition) -> Int?): Flow<Set<Int>> =
|
||||
map { it.mapNotNullTo(mutableSetOf(), mapper) }
|
||||
|
||||
fun Flow<Set<GitLabNotePosition>>.toLocations(mapper: (GitLabNotePosition) -> DiffLineLocation?): Flow<Set<DiffLineLocation>> =
|
||||
fun Flow<Set<GitLabNotePosition>>.toLocations(mapper: (GitLabNotePosition) -> GitLabNoteLocation?): Flow<Set<GitLabNoteLocation>> =
|
||||
map { it.mapNotNullTo(mutableSetOf(), mapper) }
|
||||
Reference in New Issue
Block a user