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)
}
}
}