IJPL-161819 inline prompt detection:

* add context to generation prompt
* implement support for html, yaml, json
* remove progress bar from generation status bar
* change UX to highlight regeneration / prompt clarification after generation done
* refactor code

GitOrigin-RevId: c87d4dd1302e44a281d86d7f7861d6b2c94ef29f
This commit is contained in:
Dmitry Batkovich
2024-09-24 07:25:58 +02:00
committed by intellij-monorepo-bot
parent a683a9eb90
commit a2a9a816a9
9 changed files with 69 additions and 56 deletions

View File

@@ -2248,9 +2248,14 @@ f:com.intellij.ide.scratch.ScratchUtil
- s:getRelativePath(com.intellij.openapi.project.Project,com.intellij.openapi.vfs.VirtualFile):java.lang.String
- s:isScratch(com.intellij.openapi.vfs.VirtualFile):Z
f:com.intellij.inlinePrompt.InlinePrompt
- *sf:isInlinePromptGenerating(com.intellij.openapi.editor.Editor):Z
- *sf:isInlinePromptGenerating(com.intellij.openapi.editor.Editor,java.lang.Integer):Z
- *sf:isInlinePromptGenerating(com.intellij.openapi.editor.Editor,java.lang.Integer,com.intellij.openapi.project.Project):Z
- *bs:isInlinePromptGenerating$default(com.intellij.openapi.editor.Editor,java.lang.Integer,com.intellij.openapi.project.Project,I,java.lang.Object):Z
- *sf:isInlinePromptShown(com.intellij.openapi.editor.Editor):Z
- *sf:isInlinePromptShown(com.intellij.openapi.editor.Editor,com.intellij.openapi.project.Project):Z
- *bs:isInlinePromptShown$default(com.intellij.openapi.editor.Editor,com.intellij.openapi.project.Project,I,java.lang.Object):Z
- *sf:isInlinePromptShown(com.intellij.openapi.editor.Editor,java.lang.Integer):Z
- *sf:isInlinePromptShown(com.intellij.openapi.editor.Editor,java.lang.Integer,com.intellij.openapi.project.Project):Z
- *bs:isInlinePromptShown$default(com.intellij.openapi.editor.Editor,java.lang.Integer,com.intellij.openapi.project.Project,I,java.lang.Object):Z
f:com.intellij.lang.BracePair
- <init>(com.intellij.psi.tree.IElementType,com.intellij.psi.tree.IElementType,Z):V
- getLeftBraceType():com.intellij.psi.tree.IElementType

View File

@@ -3,7 +3,7 @@
package com.intellij.inlinePrompt
import com.intellij.inlinePrompt.InlinePromptManager.Companion.getInstance
import com.intellij.openapi.components.service
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.project.Project
import org.jetbrains.annotations.ApiStatus.Experimental
@@ -18,17 +18,13 @@ import javax.swing.Icon
*/
@Internal
interface InlinePromptManager {
companion object {
fun getInstance(project: Project): InlinePromptManager = project.getService(InlinePromptManager::class.java)
}
/**
* Checks if the inline prompt is currently shown in the specified editor.
*
* @param editor the editor in which the inline prompt visibility is to be checked
* @return true if the inline prompt is shown, false otherwise
*/
fun isInlinePromptShown(editor: Editor): Boolean
fun isInlinePromptShown(editor: Editor, line: Int?): Boolean
/**
* Checks if inline prompt code is currently being generated in the specified editor.
@@ -36,7 +32,7 @@ interface InlinePromptManager {
* @param editor the editor in which the code generation status of the inline prompt is to be checked
* @return true if inline prompt code is being generated, false otherwise
*/
fun isInlinePromptCodeGenerating(editor: Editor): Boolean
fun isInlinePromptCodeGenerating(editor: Editor, line: Int?): Boolean
fun getBulbIcon(): Icon?
}
@@ -45,14 +41,30 @@ interface InlinePromptManager {
* Checks if the inline prompt is currently shown in the specified editor.
*
* @param editor the editor in which the inline prompt visibility is to be checked
* @param line the line which is inspected for having inline prompt. If `null`, then the function all lines are checked
* @param project the project associated with the editor, defaults to the project of the editor if not provided
* @return true if the inline prompt is shown, false otherwise
*/
@Experimental
@JvmOverloads
fun isInlinePromptShown(editor: Editor, project: Project? = editor.project): Boolean {
fun isInlinePromptShown(editor: Editor, line: Int? = null, project: Project? = editor.project): Boolean {
if (project == null) return false
return getInstance(project).isInlinePromptShown(editor)
return project.service<InlinePromptManager>().isInlinePromptShown(editor, line)
}
/**
* Checks if the inline prompt generation UI elements are active.
*
* @param editor the editor in which the inline prompt visibility is to be checked
* @param line the line which is inspected for having active generation UI. If `null`, then the function all lines are checked
* @param project the project associated with the editor, defaults to the project of the editor if not provided
* @return true if the inline prompt is shown, false otherwise
*/
@Experimental
@JvmOverloads
fun isInlinePromptGenerating(editor: Editor, line: Int? = null, project: Project? = editor.project): Boolean {
if (project == null) return false
return project.service<InlinePromptManager>().isInlinePromptCodeGenerating(editor, line)
}
/**
@@ -60,7 +72,7 @@ fun isInlinePromptShown(editor: Editor, project: Project? = editor.project): Boo
*/
@Internal
fun getInlinePromptBulbIcon(project: Project, editor: Editor): Icon? {
val inlinePromptManager = getInstance(project)
val isInlinePrompt = inlinePromptManager.isInlinePromptShown(editor) || inlinePromptManager.isInlinePromptCodeGenerating(editor)
val inlinePromptManager = project.service<InlinePromptManager>()
val isInlinePrompt = inlinePromptManager.isInlinePromptShown(editor, null) || inlinePromptManager.isInlinePromptCodeGenerating(editor, null)
return if (isInlinePrompt) inlinePromptManager.getBulbIcon() else null
}

View File

@@ -5,9 +5,9 @@ import com.intellij.openapi.editor.Editor
import javax.swing.Icon
internal class EmptyInlinePromptManager : InlinePromptManager {
override fun isInlinePromptShown(editor: Editor): Boolean = false
override fun isInlinePromptShown(editor: Editor, line: Int?): Boolean = false
override fun isInlinePromptCodeGenerating(editor: Editor): Boolean = false
override fun isInlinePromptCodeGenerating(editor: Editor, line: Int?): Boolean = false
override fun getBulbIcon(): Icon? = null
}

View File

@@ -1327,21 +1327,11 @@ f:com.intellij.collaboration.ui.codereview.diff.viewer.DiffViewerUtilKt
- findRange(com.intellij.openapi.vcs.ex.Range):com.intellij.openapi.vcs.ex.Range
- getRanges():java.util.List
- a:getReviewRanges():kotlinx.coroutines.flow.StateFlow
*c:com.intellij.collaboration.ui.codereview.editor.CodeReviewEditorGutterChangesRenderer
*Fc:com.intellij.collaboration.ui.codereview.editor.CodeReviewEditorGutterChangesRenderer
- com.intellij.openapi.vcs.ex.LineStatusMarkerRendererWithPopup
- *sf:Companion:com.intellij.collaboration.ui.codereview.editor.CodeReviewEditorGutterChangesRenderer$Companion
- <init>(com.intellij.collaboration.ui.codereview.editor.CodeReviewEditorGutterActionableChangesModel,com.intellij.openapi.editor.Editor,com.intellij.openapi.Disposable,com.intellij.openapi.diff.LineStatusMarkerColorScheme):V
- b:<init>(com.intellij.collaboration.ui.codereview.editor.CodeReviewEditorGutterActionableChangesModel,com.intellij.openapi.editor.Editor,com.intellij.openapi.Disposable,com.intellij.openapi.diff.LineStatusMarkerColorScheme,I,kotlin.jvm.internal.DefaultConstructorMarker):V
- p:createCopyLineAction(com.intellij.openapi.vcs.ex.Range):com.intellij.openapi.actionSystem.AnAction
- p:createErrorStripeTextAttributes(B):com.intellij.openapi.editor.markup.TextAttributes
- p:createNextChangeAction(com.intellij.openapi.vcs.ex.Range):com.intellij.openapi.actionSystem.AnAction
- p:createPopupPanel(com.intellij.openapi.editor.Editor,com.intellij.openapi.vcs.ex.Range,java.awt.Point,com.intellij.openapi.Disposable):com.intellij.openapi.vcs.ex.LineStatusMarkerPopupPanel
- p:createPrevChangeAction(com.intellij.openapi.vcs.ex.Range):com.intellij.openapi.actionSystem.AnAction
- p:createRevertAction(com.intellij.openapi.vcs.ex.Range):com.intellij.openapi.actionSystem.AnAction
- p:createShowDiffAction(com.intellij.openapi.vcs.ex.Range):com.intellij.openapi.actionSystem.AnAction
- p:createToggleByWordDiffAction():com.intellij.openapi.actionSystem.AnAction
- f:getLineStatusMarkerColorScheme():com.intellij.openapi.diff.LineStatusMarkerColorScheme
- p:paintGutterMarkers(com.intellij.openapi.editor.Editor,java.util.List,java.awt.Graphics):V
*f:com.intellij.collaboration.ui.codereview.editor.CodeReviewEditorGutterChangesRenderer$Companion
- f:render(com.intellij.collaboration.ui.codereview.editor.CodeReviewEditorGutterActionableChangesModel,com.intellij.openapi.editor.Editor,kotlin.coroutines.Continuation):java.lang.Object
- f:setupIn(kotlinx.coroutines.CoroutineScope,com.intellij.collaboration.ui.codereview.editor.CodeReviewEditorGutterActionableChangesModel,com.intellij.openapi.editor.Editor):V

View File

@@ -35,7 +35,7 @@ import com.intellij.openapi.vcs.ex.LineStatusMarkerRendererWithPopup
import com.intellij.openapi.vcs.ex.Range
import com.intellij.ui.EditorTextField
import kotlinx.coroutines.*
import org.jetbrains.annotations.ApiStatus.Internal
import org.jetbrains.annotations.ApiStatus
import java.awt.Color
import java.awt.Graphics
import java.awt.Point
@@ -44,11 +44,12 @@ import java.awt.datatransfer.StringSelection
/**
* Draws and handles review changes markers in gutter
*/
@ApiStatus.NonExtendable
open class CodeReviewEditorGutterChangesRenderer(
private val model: CodeReviewEditorGutterActionableChangesModel,
private val editor: Editor,
protected val model: CodeReviewEditorGutterActionableChangesModel,
protected val editor: Editor,
disposable: Disposable,
val lineStatusMarkerColorScheme: LineStatusMarkerColorScheme = ReviewInEditorUtil.REVIEW_STATUS_MARKER_COLOR_SCHEME,
private val lineStatusMarkerColorScheme: LineStatusMarkerColorScheme = ReviewInEditorUtil.REVIEW_STATUS_MARKER_COLOR_SCHEME,
) : LineStatusMarkerRendererWithPopup(editor.project, editor.document, model, disposable, { it === editor }) {
override fun paintGutterMarkers(editor: Editor, ranges: List<Range>, g: Graphics) {
@@ -69,7 +70,7 @@ open class CodeReviewEditorGutterChangesRenderer(
disposable: Disposable): LineStatusMarkerPopupPanel {
val vcsContent = model.getBaseContent(LineRange(range.vcsLine1, range.vcsLine2))?.removeSuffix("\n")
val editorComponent = if (!vcsContent.isNullOrEmpty()) {
val editorComponent = if (vcsContent != null) {
val popupEditor = createPopupEditor(project, editor, vcsContent, disposable)
showLineDiff(editor, popupEditor, range, vcsContent, disposable)
LineStatusMarkerPopupPanel.createEditorComponent(editor, popupEditor.component)
@@ -78,28 +79,21 @@ open class CodeReviewEditorGutterChangesRenderer(
null
}
val actions = listOfNotNull(
createRevertAction(range),
createPrevChangeAction(range),
createNextChangeAction(range),
createCopyLineAction(range),
createShowDiffAction(range),
createToggleByWordDiffAction()
) + customActions(project)
val actions = createActions(range)
val toolbar = LineStatusMarkerPopupPanel.buildToolbar(editor, actions, disposable)
return LineStatusMarkerPopupPanel.create(editor, toolbar, editorComponent, null)
}
@Internal
protected open fun customActions(project: Project?): List<AnAction> = emptyList()
protected open fun createRevertAction(range: Range): AnAction? = null
protected open fun createPrevChangeAction(range: Range): AnAction? = ShowPrevChangeMarkerAction(range)
protected open fun createNextChangeAction(range: Range): AnAction? = ShowNextChangeMarkerAction(range)
protected open fun createCopyLineAction(range: Range): AnAction? = CopyLineStatusRangeAction(range)
protected open fun createShowDiffAction(range: Range): AnAction? = ShowDiffAction(range)
protected open fun createToggleByWordDiffAction(): AnAction? = ToggleByWordDiffAction()
protected open fun createActions(range: Range): List<AnAction> {
return listOf(
ShowPrevChangeMarkerAction(range),
ShowNextChangeMarkerAction(range),
CopyLineStatusRangeAction(range),
ShowDiffAction(range),
ToggleByWordDiffAction()
)
}
private fun createPopupEditor(project: Project?, mainEditor: Editor, vcsContent: String, disposable: Disposable): Editor {
val factory = EditorFactory.getInstance()
@@ -140,8 +134,6 @@ open class CodeReviewEditorGutterChangesRenderer(
var highlightersDisposable: Disposable? = null
fun update(show: Boolean) {
if (show && highlightersDisposable == null) {
if (vcsContent.isEmpty()) return
val currentContent = DiffUtil.getLinesContent(editor.document, range.line1, range.line2)
if (currentContent.isEmpty()) return
@@ -175,7 +167,7 @@ open class CodeReviewEditorGutterChangesRenderer(
update(model.shouldHighlightDiffRanges)
}
private inner class ShowNextChangeMarkerAction(range: Range)
protected inner class ShowNextChangeMarkerAction(range: Range)
: LineStatusMarkerPopupActions.RangeMarkerAction(editor, rangesSource, range, "VcsShowNextChangeMarker"), LightEditCompatible {
override fun isEnabled(editor: Editor, range: Range): Boolean = getNextRange(range.line1) != null
@@ -193,7 +185,7 @@ open class CodeReviewEditorGutterChangesRenderer(
}
}
private inner class ShowPrevChangeMarkerAction(range: Range)
protected inner class ShowPrevChangeMarkerAction(range: Range)
: LineStatusMarkerPopupActions.RangeMarkerAction(editor, rangesSource, range, "VcsShowPrevChangeMarker"), LightEditCompatible {
override fun isEnabled(editor: Editor, range: Range): Boolean = getPrevRange(range.line1) != null
@@ -211,7 +203,7 @@ open class CodeReviewEditorGutterChangesRenderer(
}
}
private inner class CopyLineStatusRangeAction(range: Range)
protected inner class CopyLineStatusRangeAction(range: Range)
: LineStatusMarkerPopupActions.RangeMarkerAction(editor, rangesSource, range, IdeActions.ACTION_COPY), LightEditCompatible {
override fun isEnabled(editor: Editor, range: Range): Boolean = range.hasVcsLines()
override fun actionPerformed(editor: Editor, range: Range) {
@@ -220,7 +212,7 @@ open class CodeReviewEditorGutterChangesRenderer(
}
}
private inner class ShowDiffAction(range: Range)
protected inner class ShowDiffAction(range: Range)
: LineStatusMarkerPopupActions.RangeMarkerAction(editor, rangesSource, range, "Vcs.ShowDiffChangedLines"), LightEditCompatible {
init {
setShortcutSet(CompositeShortcutSet(KeymapUtil.getActiveKeymapShortcuts("Vcs.ShowDiffChangedLines"),
@@ -237,7 +229,7 @@ open class CodeReviewEditorGutterChangesRenderer(
}
}
private inner class ToggleByWordDiffAction
protected inner class ToggleByWordDiffAction
: ToggleAction(CollaborationToolsBundle.message("review.editor.action.highlight.lines.text"), null, AllIcons.Actions.Highlighting),
DumbAware, LightEditCompatible {

View File

@@ -3200,7 +3200,6 @@ a:com.intellij.openapi.vcs.ex.LineStatusMarkerRenderer
- p:createGutterMarkerRenderer():com.intellij.openapi.editor.markup.LineMarkerRenderer
- pf:getDisposable():com.intellij.openapi.Disposable
- pf:getDocument():com.intellij.openapi.editor.Document
- p:getGutterLayer():I
- pf:getProject():com.intellij.openapi.project.Project
- pa:getRanges():java.util.List
- f:scheduleUpdate():V

View File

@@ -28,6 +28,7 @@ import com.intellij.util.containers.PeekableIterator
import com.intellij.util.containers.PeekableIteratorWrapper
import com.intellij.util.ui.update.DisposableUpdate
import com.intellij.util.ui.update.MergingUpdateQueue
import org.jetbrains.annotations.ApiStatus
abstract class LineStatusMarkerRenderer internal constructor(
protected val project: Project?,
@@ -45,6 +46,12 @@ abstract class LineStatusMarkerRenderer internal constructor(
protected abstract fun getRanges(): List<Range>?
@Deprecated("""
A hack for rendering inline prompt gutter mark on top of the git's one.
The method should be removed and mark conflict should be resolved in another way.
See com.intellij.ml.llm.inlinePromptDetector.diff.CGResultGutterRenderer.
""")
@ApiStatus.Internal
protected open fun getGutterLayer(): Int {
return DiffDrawUtil.LST_LINE_MARKER_LAYER
}

View File

@@ -14,6 +14,8 @@ import com.intellij.codeInsight.codeVision.ui.popup.CodeVisionPopup
import com.intellij.codeInsight.codeVision.ui.renderers.BlockCodeVisionInlayRenderer
import com.intellij.codeInsight.codeVision.ui.renderers.CodeVisionInlayRenderer
import com.intellij.codeInsight.codeVision.ui.renderers.InlineCodeVisionInlayRenderer
import com.intellij.inlinePrompt.isInlinePromptGenerating
import com.intellij.inlinePrompt.isInlinePromptShown
import com.intellij.openapi.components.Service
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.editor.Inlay
@@ -72,7 +74,9 @@ class CodeVisionView(val project: Project) {
val listInlays = mutableListOf<Inlay<*>>()
for (lens in lenses) {
val position = if (lens.key == CodeVisionAnchorKind.Default) CodeVisionSettings.getInstance().defaultPosition else lens.key
val logicalPosition = editor.offsetToLogicalPosition(anchoringRange.startOffset)
if (position == CodeVisionAnchorKind.Right && (isInlinePromptShown(editor, line = logicalPosition.line)) || isInlinePromptGenerating(editor, line = logicalPosition.line)) continue
val inlay = when (if (lens.key == CodeVisionAnchorKind.Default) CodeVisionSettings.getInstance().defaultPosition else lens.key) {
CodeVisionAnchorKind.Top -> {
getOrCreateBlockInlay(editor, anchoringRange)

View File

@@ -9,6 +9,7 @@ import com.intellij.find.FindManager;
import com.intellij.find.findUsages.FindUsagesHandler;
import com.intellij.find.findUsages.FindUsagesManager;
import com.intellij.find.impl.FindManagerImpl;
import com.intellij.inlinePrompt.InlinePrompt;
import com.intellij.lang.injection.InjectedLanguageManager;
import com.intellij.model.Symbol;
import com.intellij.openapi.application.ApplicationManager;
@@ -73,6 +74,9 @@ public final class IdentifierHighlighterPass {
}
public void doCollectInformation(@NotNull HighlightingSession hostSession) {
if (InlinePrompt.isInlinePromptShown(myEditor)) {
return;
}
ApplicationManager.getApplication().assertIsNonDispatchThread();
ApplicationManager.getApplication().assertReadAccessAllowed();
HighlightUsagesHandlerBase<PsiElement> highlightUsagesHandler =