[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:
Bartosz Janusz
2026-02-05 17:00:52 +00:00
committed by intellij-monorepo-bot
parent ede55ae0f7
commit a3964f5d0d
17 changed files with 291 additions and 148 deletions
@@ -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 {
@@ -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
@@ -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,
)
@@ -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)
}
@@ -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?,
@@ -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)
}
@@ -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()
}
@@ -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
@@ -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(
@@ -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 }
}
@@ -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)
}
@@ -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())
@@ -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)
}
@@ -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)
}
)
@@ -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)
@@ -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,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) }