fun showPopup()

in plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/popup/CodeWhispererPopupManager.kt [280:388]


    fun showPopup(
        states: InvocationContext,
        sessionContext: SessionContext,
        popup: JBPopup,
        overlappingLinesCount: Int,
    ) {
        val caretPoint = states.requestContext.editor.offsetToXY(states.requestContext.caretPosition.offset)
        val editor = states.requestContext.editor
        val detailContexts = states.recommendationContext.details
        val userInputOriginal = states.recommendationContext.userInputOriginal
        val userInput = states.recommendationContext.userInputSinceInvocation
        val selectedIndex = sessionContext.selectedIndex
        val typeaheadOriginal = sessionContext.typeaheadOriginal
        val typeahead = sessionContext.typeahead
        val userInputLines = userInputOriginal.split("\n").size - 1
        val lineCount = getReformattedRecommendation(detailContexts[selectedIndex], userInput).split("\n").size
        val additionalLines = typeaheadOriginal.split("\n").size - typeahead.split("\n").size
        val popupSize = (popup as AbstractPopup).preferredContentSize
        val yBelowLastLine = caretPoint.y + (lineCount + additionalLines + userInputLines - overlappingLinesCount) * editor.lineHeight
        val yAboveFirstLine = caretPoint.y - popupSize.height + (additionalLines + userInputLines) * editor.lineHeight
        val editorRect = editor.scrollingModel.visibleArea
        var popupRect = Rectangle(caretPoint.x, yBelowLastLine, popupSize.width, popupSize.height)
        var noEnoughSpaceForPopup = false

        CodeWhispererInvocationStatus.getInstance().setDisplaySessionActive(true)

        // Check if the current editor still has focus. If not, don't show the popup.
        val isSameEditorAsTrigger = if (!AppMode.isRemoteDevHost()) {
            editor.contentComponent.isFocusOwner
        } else {
            FileEditorManager.getInstance(states.requestContext.project).selectedTextEditorWithRemotes.firstOrNull() == editor
        }
        if (!isSameEditorAsTrigger) {
            LOG.debug { "Current editor no longer has focus, not showing the popup" }
            cancelPopup(popup)
            return
        }

        val popupLocation =
            if (!editorRect.contains(popupRect)) {
                popupRect = Rectangle(caretPoint.x, yAboveFirstLine, popupSize.width, popupSize.height)
                if (!editorRect.contains(popupRect)) {
                    // both popup location (below last line and above first line) don't work, so don't show the popup
                    noEnoughSpaceForPopup = true
                }
                LOG.debug {
                    "Show popup above the first line of recommendation. " +
                        "Editor position: $editorRect, popup position: $popupRect"
                }
                Point(caretPoint.x, yAboveFirstLine)
            } else {
                LOG.debug {
                    "Show popup below the last line of recommendation. " +
                        "Editor position: $editorRect, popup position: $popupRect"
                }
                Point(caretPoint.x, yBelowLastLine)
            }

        val relativePopupLocationToEditor = RelativePoint(editor.contentComponent, popupLocation)

        // TODO: visibleAreaChanged listener is not getting triggered in remote environment when scrolling
        if (popup.isVisible) {
            // Changing the position of BackendBeAbstractPopup does not work
            if (!noEnoughSpaceForPopup && !AppMode.isRemoteDevHost()) {
                popup.setLocation(relativePopupLocationToEditor.screenPoint)
                popup.size = popup.preferredContentSize
            }
        } else {
            if (!AppMode.isRemoteDevHost()) {
                popupComponents.prevButton.text = popupComponents.prevButtonText()
                popupComponents.nextButton.text = popupComponents.nextButtonText()
                popup.show(relativePopupLocationToEditor)
            } else {
                // TODO: For now, the popup will always display below the suggestions, without checking
                // if the location the popup is about to show at stays in the editor window or not, due to
                // the limitation of BackendBeAbstractPopup
                val caretVisualPosition = editor.offsetToVisualPosition(editor.caretModel.offset)

                // display popup x lines below the caret where x is # of lines of suggestions, since inlays don't
                // count as visual lines, the final math will always be just increment 1 line.
                val popupPositionForRemote = VisualPosition(
                    caretVisualPosition.line + 1,
                    caretVisualPosition.column
                )
                editor.putUserData(PopupFactoryImpl.ANCHOR_POPUP_POSITION, popupPositionForRemote)
                popup.showInBestPositionFor(editor)
            }
            if (sessionContext.perceivedLatency < 0) {
                val perceivedLatency = CodeWhispererInvocationStatus.getInstance().getTimeSinceDocumentChanged()
                CodeWhispererTelemetryService.getInstance().sendPerceivedLatencyEvent(
                    detailContexts[selectedIndex].requestId,
                    states.requestContext,
                    states.responseContext,
                    perceivedLatency
                )
                CodeWhispererTelemetryService.getInstance().sendClientComponentLatencyEvent(states)
                sessionContext.perceivedLatency = perceivedLatency
            }
        }

        // popup.popupWindow is null in remote host
        if (!AppMode.isRemoteDevHost()) {
            if (noEnoughSpaceForPopup) {
                WindowManager.getInstance().setAlphaModeRatio(popup.popupWindow, 1f)
            } else {
                WindowManager.getInstance().setAlphaModeRatio(popup.popupWindow, 0.1f)
            }
        }
    }