[gh] Add a new way of setuping multiline comment in the diff PR review by dragging frame

#IJPL-200028 Fixed

GitOrigin-RevId: 4ecff4edaab3488c15e6e8df6fa669313e627981
This commit is contained in:
Bartosz Janusz
2025-09-30 10:55:58 +00:00
committed by intellij-monorepo-bot
parent a236de732f
commit ed95556209
3 changed files with 42 additions and 23 deletions
@@ -10,6 +10,7 @@ import com.intellij.collaboration.util.ComputedResult
import com.intellij.collaboration.util.RefComparisonChange
import com.intellij.collaboration.util.filePath
import com.intellij.collaboration.util.getOrNull
import com.intellij.diff.util.LineRange
import com.intellij.diff.util.Range
import com.intellij.diff.util.Side
import com.intellij.openapi.diff.impl.patch.PatchHunkUtil
@@ -47,6 +48,7 @@ interface GHPRDiffReviewViewModel {
fun requestNewComment(location: GHPRReviewCommentLocation, focus: Boolean)
fun cancelNewComment(side: Side, lineIdx: Int)
fun updateCommentLines(oldLineRange: LineRange, newLineRange: LineRange)
val isViewedState: StateFlow<ComputedResult<Boolean>>
fun setViewedState(isViewed: Boolean)
@@ -97,7 +99,7 @@ internal class GHPRDiffReviewViewModelImpl(
private val newCommentsContainer =
MappingScopedItemsContainer.byIdentity<GHPRReviewNewCommentEditorViewModel, GHPRNewCommentDiffViewModelImpl>(cs) {
GHPRNewCommentDiffViewModelImpl(it.position.value.location, it)
GHPRNewCommentDiffViewModelImpl(it.position, it)
}
override val newComments: StateFlow<Collection<GHPRNewCommentDiffViewModel>> =
newCommentsContainer.mappingState.mapState { it.values }
@@ -131,6 +133,14 @@ internal class GHPRDiffReviewViewModelImpl(
threadsVms.cancelNewComment(change, side, lineIdx)
override fun updateCommentLines(oldLineRange: LineRange, newLineRange: LineRange) =
threadsVms.newComments.value.firstOrNull {
when (val loc = it.position.value.location) {
is GHPRReviewCommentLocation.SingleLine -> loc.lineIdx == oldLineRange.end
is GHPRReviewCommentLocation.MultiLine -> loc.startLineIdx == oldLineRange.start && loc.lineIdx == oldLineRange.end
}
}?.updateLineRange(newLineRange) ?: Unit
override val isViewedState: StateFlow<ComputedResult<Boolean>> =
dataProvider.viewedStateData.viewedStateComputationState
.map { viewedStateByPath ->
@@ -1,25 +1,28 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package org.jetbrains.plugins.github.pullrequest.ui.diff
import com.intellij.collaboration.async.mapState
import kotlinx.coroutines.channels.BufferOverflow
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.receiveAsFlow
import org.jetbrains.plugins.github.pullrequest.ui.comment.GHPRReviewCommentLocation
import org.jetbrains.plugins.github.pullrequest.ui.comment.GHPRReviewCommentPosition
import org.jetbrains.plugins.github.pullrequest.ui.editor.GHPRReviewNewCommentEditorViewModel
interface GHPRNewCommentDiffViewModel : GHPRReviewNewCommentEditorViewModel {
val location: GHPRReviewCommentLocation
val location: StateFlow<GHPRReviewCommentLocation>
}
internal class GHPRNewCommentDiffViewModelImpl(
override val location: GHPRReviewCommentLocation,
position: StateFlow<GHPRReviewCommentPosition>,
private val sharedVm: GHPRReviewNewCommentEditorViewModel,
)
: GHPRNewCommentDiffViewModel, GHPRReviewNewCommentEditorViewModel by sharedVm {
private val _focusRequestsChannel = Channel<Unit>(1, BufferOverflow.DROP_OLDEST)
override val focusRequests: Flow<Unit> get() = _focusRequestsChannel.receiveAsFlow()
override val location = position.mapState { it.location }
override fun requestFocus() {
_focusRequestsChannel.trySend(Unit)
}
@@ -29,10 +29,8 @@ import com.intellij.util.cancelOnDispose
import com.intellij.util.concurrency.annotations.RequiresEdt
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.withContext
import org.jetbrains.plugins.github.ai.GHPRAICommentViewModel
import org.jetbrains.plugins.github.pullrequest.ui.GHPRInlayUtils
@@ -137,10 +135,18 @@ private class DiffEditorModel(
override val inlays: StateFlow<Collection<GHPREditorMappedComponentModel>> =
combineStateIn(cs, threads, newComments, aiComments) { threads, new, ai -> threads + new + ai }
@OptIn(ExperimentalCoroutinesApi::class)
private val linesWithNewCommentsFlow: StateFlow<Set<Int>> =
diffVm.newComments.flatMapLatest { vms ->
if (vms.isEmpty()) flowOf(emptySet())
else combine(vms.map { it.location.map { loc -> locationToLine(loc.lineLocation) } }) { lines ->
lines.filterNotNull().toSet()
}
}.stateInNow(cs, emptySet())
override val gutterControlsState: StateFlow<CodeReviewEditorGutterControlsModel.ControlsState?> =
combine(diffVm.locationsWithDiscussions, diffVm.newComments) { locationsWithDiscussions, newComments ->
combine(diffVm.locationsWithDiscussions, linesWithNewCommentsFlow) { locationsWithDiscussions, linesWithNewComments ->
val linesWithComments = locationsWithDiscussions.mapNotNullTo(mutableSetOf(), locationToLine)
val linesWithNewComments = newComments.mapNotNullTo(mutableSetOf()) { locationToLine(it.location.lineLocation) }
GHPRReviewEditorGutterControlsState(
linesWithComments, linesWithNewComments,
if (diffVm.canComment) transferRanges(diffVm.commentableRanges, diffVm.changedRanges) else emptyList()
@@ -201,7 +207,7 @@ private class DiffEditorModel(
}
override fun updateCommentLines(oldLineRange: LineRange, newLineRange: LineRange) {
TODO("Not yet implemented")
diffVm.updateCommentLines(LineRange(oldLineRange.start, oldLineRange.end), LineRange(newLineRange.start, newLineRange.end))
}
override fun requestNewComment(lineIdx: Int) {
@@ -264,21 +270,21 @@ private class DiffEditorModel(
private inner class MappedNewComment(vm: GHPRNewCommentDiffViewModel)
: GHPREditorMappedComponentModel.NewComment<GHPRReviewNewCommentEditorViewModel>(vm) {
private val location = vm.position.value.location.lineLocation.let { it.first to it.second }
private val location: StateFlow<GHPRReviewCommentLocation> = vm.position.mapState { it.location }
override val key: Any = "NEW_${vm.position.value.location}"
override val isVisible: StateFlow<Boolean> = MutableStateFlow(true)
override val line: StateFlow<Int?> = MutableStateFlow(locationToLine(location))
override val range: StateFlow<Pair<Side, IntRange>?> = MutableStateFlow(when (val loc = vm.location) {
is GHPRReviewCommentLocation.SingleLine -> {
loc.side to loc.lineIdx..loc.lineIdx
}
is GHPRReviewCommentLocation.MultiLine -> {
loc.side to loc.startLineIdx..loc.lineIdx
}
})
override val line: StateFlow<Int?> = location.mapState { locationToLine(it.lineLocation) }
private val _range = MutableStateFlow<Pair<Side, IntRange>?>(when (val loc = location.value) {
is GHPRReviewCommentLocation.SingleLine -> {
loc.side to loc.lineIdx..loc.lineIdx
}
is GHPRReviewCommentLocation.MultiLine -> {
loc.side to loc.startLineIdx..loc.lineIdx
}
})
override val range: StateFlow<Pair<Side, IntRange>?> = _range.asStateFlow()
override fun setRange(range: Pair<Side, IntRange>?) {
TODO("Not yet implemented")
_range.value = range
}
}