Skip to content

Commit 464233b

Browse files
authored
Merge branch 'feature/build-execute' into randal/build
2 parents 6e2a222 + 154c5f0 commit 464233b

File tree

13 files changed

+94
-148
lines changed

13 files changed

+94
-148
lines changed

.changes/3.58.json

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"date" : "2025-03-06",
3+
"version" : "3.58",
4+
"entries" : [ {
5+
"type" : "bugfix",
6+
"description" : "Amazon Q: Fix data isolation between tabs to prevent interference when using /doc in multiple tabs"
7+
}, {
8+
"type" : "removal",
9+
"description" : "The Amazon Q inline suggestion popup goes back to being under the suggestions and is always showing."
10+
} ]
11+
}

.changes/next-release/bugfix-79596dfc-37a0-44c8-adf2-a6c87ba806ac.json

-4
This file was deleted.

CHANGELOG.md

+4
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
# _3.58_ (2025-03-06)
2+
- **(Bug Fix)** Amazon Q: Fix data isolation between tabs to prevent interference when using /doc in multiple tabs
3+
- **(Removal)** The Amazon Q inline suggestion popup goes back to being under the suggestions and is always showing.
4+
15
# _3.57_ (2025-02-28)
26
- **(Bug Fix)** Fix suggestion not visible in remote for 2024.3
37
- **(Bug Fix)** /test: update capability card text

CONTRIBUTING.md

+1
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ reported the issue. Please try to include as much information as you can. Detail
3131
brew install dotnet@6
3232
```
3333
* If Gradle cannot find `dotnet`, run `./gradlew --stop` and `./gradlew projects` to reload the daemon. Note that this should be done in your terminal as invoking Gradle through the IDE will use the IDE's cached PATH.
34+
* It is recommended to [launch your IDE from the terminal](https://www.jetbrains.com/help/idea/working-with-the-ide-features-from-command-line.html) due to a known issue with Gradle/Java 21 where the Gradle daemon does not respect your PATH variable when the IDE is started from the desktop/Toolbox.
3435
3536
### Instructions
3637

gradle.properties

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
# SPDX-License-Identifier: Apache-2.0
33

44
# Toolkit Version
5-
toolkitVersion=3.58-SNAPSHOT
5+
toolkitVersion=3.59-SNAPSHOT
66

77
# Publish Settings
88
publishToken=

plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/popup/CodeWhispererPopupManager.kt

+36-60
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ package software.aws.toolkits.jetbrains.services.codewhisperer.popup
55

66
import com.intellij.codeInsight.hint.ParameterInfoController
77
import com.intellij.codeInsight.lookup.LookupManager
8-
import com.intellij.codeInsight.lookup.LookupManagerListener
98
import com.intellij.idea.AppMode
109
import com.intellij.openapi.actionSystem.IdeActions.ACTION_EDITOR_ENTER
1110
import com.intellij.openapi.actionSystem.IdeActions.ACTION_EDITOR_ESCAPE
@@ -26,8 +25,6 @@ import com.intellij.openapi.editor.event.CaretEvent
2625
import com.intellij.openapi.editor.event.CaretListener
2726
import com.intellij.openapi.editor.event.DocumentEvent
2827
import com.intellij.openapi.editor.event.DocumentListener
29-
import com.intellij.openapi.editor.event.EditorMouseEvent
30-
import com.intellij.openapi.editor.event.EditorMouseMotionListener
3128
import com.intellij.openapi.editor.event.SelectionEvent
3229
import com.intellij.openapi.editor.event.SelectionListener
3330
import com.intellij.openapi.fileEditor.FileEditorManager
@@ -65,10 +62,8 @@ import software.aws.toolkits.jetbrains.services.codewhisperer.popup.handlers.Cod
6562
import software.aws.toolkits.jetbrains.services.codewhisperer.popup.listeners.CodeWhispererAcceptButtonActionListener
6663
import software.aws.toolkits.jetbrains.services.codewhisperer.popup.listeners.CodeWhispererActionListener
6764
import software.aws.toolkits.jetbrains.services.codewhisperer.popup.listeners.CodeWhispererNextButtonActionListener
68-
import software.aws.toolkits.jetbrains.services.codewhisperer.popup.listeners.CodeWhispererPopupIntelliSenseAcceptListener
6965
import software.aws.toolkits.jetbrains.services.codewhisperer.popup.listeners.CodeWhispererPrevButtonActionListener
7066
import software.aws.toolkits.jetbrains.services.codewhisperer.popup.listeners.CodeWhispererScrollListener
71-
import software.aws.toolkits.jetbrains.services.codewhisperer.popup.listeners.addIntelliSenseAcceptListener
7267
import software.aws.toolkits.jetbrains.services.codewhisperer.service.CodeWhispererInvocationStatus
7368
import software.aws.toolkits.jetbrains.services.codewhisperer.telemetry.CodeWhispererTelemetryService
7469
import software.aws.toolkits.jetbrains.services.codewhisperer.toolwindow.CodeWhispererCodeReferenceManager
@@ -224,6 +219,7 @@ class CodeWhispererPopupManager {
224219
fun render(
225220
states: InvocationContext,
226221
sessionContext: SessionContext,
222+
overlappingLinesCount: Int,
227223
isRecommendationAdded: Boolean,
228224
) {
229225
updatePopupPanel(states, sessionContext)
@@ -246,7 +242,7 @@ class CodeWhispererPopupManager {
246242
states.requestContext.latencyContext.getPerceivedLatency(states.requestContext.triggerTypeInfo.triggerType)
247243
}
248244
if (!isRecommendationAdded) {
249-
showPopup(states, sessionContext, states.popup, visible = sessionContext.isPopupShowing)
245+
showPopup(states, sessionContext, states.popup, overlappingLinesCount)
250246
}
251247
}
252248

@@ -285,30 +281,28 @@ class CodeWhispererPopupManager {
285281
states: InvocationContext,
286282
sessionContext: SessionContext,
287283
popup: JBPopup,
288-
visible: Boolean = false,
284+
overlappingLinesCount: Int,
289285
) {
290286
val caretPoint = states.requestContext.editor.offsetToXY(states.requestContext.caretPosition.offset)
291287
val editor = states.requestContext.editor
292288
val detailContexts = states.recommendationContext.details
293289
val userInputOriginal = states.recommendationContext.userInputOriginal
290+
val userInput = states.recommendationContext.userInputSinceInvocation
294291
val selectedIndex = sessionContext.selectedIndex
295292
val typeaheadOriginal = sessionContext.typeaheadOriginal
296293
val typeahead = sessionContext.typeahead
297294
val userInputLines = userInputOriginal.split("\n").size - 1
295+
val lineCount = getReformattedRecommendation(detailContexts[selectedIndex], userInput).split("\n").size
298296
val additionalLines = typeaheadOriginal.split("\n").size - typeahead.split("\n").size
299297
val popupSize = (popup as AbstractPopup).preferredContentSize
298+
val yBelowLastLine = caretPoint.y + (lineCount + additionalLines + userInputLines - overlappingLinesCount) * editor.lineHeight
300299
val yAboveFirstLine = caretPoint.y - popupSize.height + (additionalLines + userInputLines) * editor.lineHeight
301300
val editorRect = editor.scrollingModel.visibleArea
302-
val popupRect = Rectangle(caretPoint.x, yAboveFirstLine, popupSize.width, popupSize.height)
301+
var popupRect = Rectangle(caretPoint.x, yBelowLastLine, popupSize.width, popupSize.height)
303302
var noEnoughSpaceForPopup = false
304303

305304
CodeWhispererInvocationStatus.getInstance().setDisplaySessionActive(true)
306305

307-
if (!editorRect.contains(popupRect)) {
308-
// popup location above first line don't work, so don't show the popup
309-
noEnoughSpaceForPopup = true
310-
}
311-
312306
// Check if the current editor still has focus. If not, don't show the popup.
313307
val isSameEditorAsTrigger = if (!AppMode.isRemoteDevHost()) {
314308
editor.contentComponent.isFocusOwner
@@ -321,8 +315,25 @@ class CodeWhispererPopupManager {
321315
return
322316
}
323317

324-
// popup to always display above the current editing line
325-
val popupLocation = Point(caretPoint.x, yAboveFirstLine)
318+
val popupLocation =
319+
if (!editorRect.contains(popupRect)) {
320+
popupRect = Rectangle(caretPoint.x, yAboveFirstLine, popupSize.width, popupSize.height)
321+
if (!editorRect.contains(popupRect)) {
322+
// both popup location (below last line and above first line) don't work, so don't show the popup
323+
noEnoughSpaceForPopup = true
324+
}
325+
LOG.debug {
326+
"Show popup above the first line of recommendation. " +
327+
"Editor position: $editorRect, popup position: $popupRect"
328+
}
329+
Point(caretPoint.x, yAboveFirstLine)
330+
} else {
331+
LOG.debug {
332+
"Show popup below the last line of recommendation. " +
333+
"Editor position: $editorRect, popup position: $popupRect"
334+
}
335+
Point(caretPoint.x, yBelowLastLine)
336+
}
326337

327338
val relativePopupLocationToEditor = RelativePoint(editor.contentComponent, popupLocation)
328339

@@ -335,12 +346,9 @@ class CodeWhispererPopupManager {
335346
}
336347
} else {
337348
if (!AppMode.isRemoteDevHost()) {
338-
if (visible && !noEnoughSpaceForPopup) {
339-
// TODO: will move to a keybinding listener once I found one
340-
popupComponents.prevButton.text = popupComponents.prevButtonText()
341-
popupComponents.nextButton.text = popupComponents.nextButtonText()
342-
popup.show(relativePopupLocationToEditor)
343-
}
349+
popupComponents.prevButton.text = popupComponents.prevButtonText()
350+
popupComponents.nextButton.text = popupComponents.nextButtonText()
351+
popup.show(relativePopupLocationToEditor)
344352
} else {
345353
// TODO: For now, the popup will always display below the suggestions, without checking
346354
// if the location the popup is about to show at stays in the editor window or not, due to
@@ -369,27 +377,14 @@ class CodeWhispererPopupManager {
369377
}
370378
}
371379

372-
bringSuggestionInlayToFront(editor, popup, sessionContext, !visible)
373-
}
374-
375-
// opposite == false: show Q, hide IntelliSense
376-
// opposite == true: show IntelliSense, hide Q
377-
fun bringSuggestionInlayToFront(
378-
editor: Editor,
379-
popup: JBPopup?,
380-
sessionContext: SessionContext,
381-
opposite: Boolean = false,
382-
) {
383-
val qInlinePopupAlpha = if (opposite) 1f else 0.1f
384-
val intelliSensePopupAlpha = if (opposite) 0f else 0.8f
385-
386-
(popup as AbstractPopup?)?.popupWindow?.let {
387-
WindowManager.getInstance().setAlphaModeRatio(it, qInlinePopupAlpha)
388-
}
389-
ComponentUtil.getWindow(LookupManager.getActiveLookup(editor)?.component)?.let {
390-
WindowManager.getInstance().setAlphaModeRatio(it, intelliSensePopupAlpha)
380+
// popup.popupWindow is null in remote host
381+
if (!AppMode.isRemoteDevHost()) {
382+
if (noEnoughSpaceForPopup) {
383+
WindowManager.getInstance().setAlphaModeRatio(popup.popupWindow, 1f)
384+
} else {
385+
WindowManager.getInstance().setAlphaModeRatio(popup.popupWindow, 0.1f)
386+
}
391387
}
392-
sessionContext.isPopupShowing = !opposite
393388
}
394389

395390
fun initPopup(): JBPopup = JBPopupFactory.getInstance()
@@ -458,13 +453,6 @@ class CodeWhispererPopupManager {
458453
}
459454
}
460455
)
461-
states.requestContext.project.messageBus.connect(states).subscribe(
462-
LookupManagerListener.TOPIC,
463-
CodeWhispererPopupIntelliSenseAcceptListener(states)
464-
)
465-
LookupManager.getActiveLookup(states.requestContext.editor)?.let {
466-
addIntelliSenseAcceptListener(it, states)
467-
}
468456
}
469457

470458
private fun addButtonActionListeners(states: InvocationContext) {
@@ -556,18 +544,6 @@ class CodeWhispererPopupManager {
556544
window?.addComponentListener(windowListener)
557545
Disposer.register(states) { window?.removeComponentListener(windowListener) }
558546
}
559-
560-
val suggestionHoverEnterListener: EditorMouseMotionListener = object : EditorMouseMotionListener {
561-
override fun mouseMoved(e: EditorMouseEvent) {
562-
if (e.inlay != null) {
563-
showPopup(states, sessionContext, states.popup, visible = true)
564-
} else {
565-
bringSuggestionInlayToFront(editor, states.popup, sessionContext, opposite = true)
566-
}
567-
super.mouseMoved(e)
568-
}
569-
}
570-
editor.addEditorMouseMotionListener(suggestionHoverEnterListener, states)
571547
}
572548

573549
private fun updateSelectedRecommendationLabelText(validSelectedIndex: Int, validCount: Int) {

plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/popup/CodeWhispererUIChangeListener.kt

+26
Original file line numberDiff line numberDiff line change
@@ -91,14 +91,39 @@ class CodeWhispererUIChangeListener : CodeWhispererPopupStateChangeListener {
9191
CodeWhispererPopupManager.getInstance().render(
9292
states,
9393
sessionContext,
94+
overlappingLinesCount,
9495
isRecommendationAdded = false
9596
)
9697
}
9798

9899
override fun scrolled(states: InvocationContext, sessionContext: SessionContext) {
100+
if (states.popup.isDisposed) return
101+
val editor = states.requestContext.editor
102+
val editorManager = CodeWhispererEditorManager.getInstance()
103+
val selectedIndex = sessionContext.selectedIndex
104+
val typeahead = sessionContext.typeahead
105+
val detail = states.recommendationContext.details[selectedIndex]
106+
107+
// get matching brackets from recommendations to the brackets after caret position
108+
val remaining = CodeWhispererPopupManager.getInstance().getReformattedRecommendation(
109+
detail,
110+
states.recommendationContext.userInputSinceInvocation
111+
).substring(typeahead.length)
112+
113+
val remainingLines = remaining.split("\n")
114+
val otherLinesOfRemaining = remainingLines.drop(1)
115+
116+
// process other lines inlays, where we do tail-head matching as much as possible
117+
val overlappingLinesCount = editorManager.findOverLappingLines(
118+
editor,
119+
otherLinesOfRemaining,
120+
detail.isTruncatedOnRight,
121+
sessionContext
122+
)
99123
CodeWhispererPopupManager.getInstance().render(
100124
states,
101125
sessionContext,
126+
overlappingLinesCount,
102127
isRecommendationAdded = false
103128
)
104129
}
@@ -107,6 +132,7 @@ class CodeWhispererUIChangeListener : CodeWhispererPopupStateChangeListener {
107132
CodeWhispererPopupManager.getInstance().render(
108133
states,
109134
sessionContext,
135+
0,
110136
isRecommendationAdded = true
111137
)
112138
}

plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/popup/listeners/CodeWhispererPopupIntelliSenseAcceptListener.kt

-60
This file was deleted.

plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/service/CodeWhispererService.kt

+2-15
Original file line numberDiff line numberDiff line change
@@ -699,7 +699,7 @@ class CodeWhispererService(private val cs: CoroutineScope) : Disposable {
699699
recommendationContext: RecommendationContext,
700700
popup: JBPopup,
701701
): InvocationContext {
702-
addPopupChildDisposables(requestContext.project, requestContext.editor, popup)
702+
addPopupChildDisposables(popup)
703703
// Creating a disposable for managing all listeners lifecycle attached to the popup.
704704
// previously(before pagination) we use popup as the parent disposable.
705705
// After pagination, listeners need to be updated as states are updated, for the same popup,
@@ -711,25 +711,12 @@ class CodeWhispererService(private val cs: CoroutineScope) : Disposable {
711711
return states
712712
}
713713

714-
private fun addPopupChildDisposables(project: Project, editor: Editor, popup: JBPopup) {
714+
private fun addPopupChildDisposables(popup: JBPopup) {
715715
codeInsightSettingsFacade.disableCodeInsightUntil(popup)
716716

717717
Disposer.register(popup) {
718718
CodeWhispererPopupManager.getInstance().reset()
719719
}
720-
project.messageBus.connect(popup).subscribe(
721-
CodeWhispererServiceNew.CODEWHISPERER_INTELLISENSE_POPUP_ON_HOVER,
722-
object : CodeWhispererIntelliSenseOnHoverListener {
723-
override fun onEnter() {
724-
CodeWhispererPopupManager.getInstance().bringSuggestionInlayToFront(
725-
editor,
726-
popup,
727-
CodeWhispererPopupManager.getInstance().sessionContext,
728-
opposite = true
729-
)
730-
}
731-
}
732-
)
733720
}
734721

735722
private fun logServiceInvocation(

0 commit comments

Comments
 (0)