Skip to content

Commit 4874197

Browse files
authored
codewhisperer: suppress token infilling for auto-trigger (#3914)
1 parent 6dc2096 commit 4874197

File tree

5 files changed

+69
-2
lines changed

5 files changed

+69
-2
lines changed

jetbrains-core/src/software/aws/toolkits/jetbrains/services/codewhisperer/editor/CodeWhispererEditorUtil.kt

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ import software.aws.toolkits.jetbrains.services.codewhisperer.language.programmi
1616
import software.aws.toolkits.jetbrains.services.codewhisperer.model.CaretContext
1717
import software.aws.toolkits.jetbrains.services.codewhisperer.model.CaretPosition
1818
import software.aws.toolkits.jetbrains.services.codewhisperer.model.FileContextInfo
19+
import software.aws.toolkits.jetbrains.services.codewhisperer.service.CodeWhispererUserGroup
20+
import software.aws.toolkits.jetbrains.services.codewhisperer.service.CodeWhispererUserGroupSettings
1921
import software.aws.toolkits.jetbrains.services.codewhisperer.util.CodeWhispererConstants
2022
import software.aws.toolkits.jetbrains.services.codewhisperer.util.CodeWhispererConstants.LEFT_CONTEXT_ON_CURRENT_LINE
2123
import java.awt.Point
@@ -90,6 +92,17 @@ object CodeWhispererEditorUtil {
9092
)
9193
}
9294

95+
fun shouldSkipInvokingBasedOnRightContext(editor: Editor): Boolean {
96+
val caretContext = runReadAction { CodeWhispererEditorUtil.extractCaretContext(editor) }
97+
val rightContextLines = caretContext.rightFileContext.split(Regex("\r?\n"))
98+
val rightContextCurrentLine = if (rightContextLines.isEmpty()) "" else rightContextLines[0]
99+
100+
return CodeWhispererUserGroupSettings.getInstance().getUserGroup() == CodeWhispererUserGroup.RightContext &&
101+
rightContextCurrentLine.isNotEmpty() &&
102+
!rightContextCurrentLine.startsWith(" ") &&
103+
rightContextCurrentLine.trim() != ("}")
104+
}
105+
93106
/**
94107
* Checks if the [otherRange] overlaps this TextRange. Note that the comparison is `<` because the endOffset of TextRange is exclusive.
95108
*/

jetbrains-core/src/software/aws/toolkits/jetbrains/services/codewhisperer/editor/CodeWhispererEnterHandler.kt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,18 @@ import com.intellij.openapi.application.ApplicationManager
99
import com.intellij.openapi.editor.Caret
1010
import com.intellij.openapi.editor.Editor
1111
import com.intellij.openapi.editor.actionSystem.EditorActionHandler
12+
import software.aws.toolkits.jetbrains.services.codewhisperer.editor.CodeWhispererEditorUtil.shouldSkipInvokingBasedOnRightContext
1213
import software.aws.toolkits.jetbrains.services.codewhisperer.service.CodeWhispererAutoTriggerService
1314
import software.aws.toolkits.jetbrains.services.codewhisperer.service.CodeWhispererAutomatedTriggerType
1415

1516
class CodeWhispererEnterHandler(private val originalHandler: EditorActionHandler) : EnterHandler(originalHandler) {
1617
override fun executeWriteAction(editor: Editor, caret: Caret?, dataContext: DataContext?) {
1718
originalHandler.execute(editor, caret, dataContext)
1819

20+
if (shouldSkipInvokingBasedOnRightContext(editor)) {
21+
return
22+
}
23+
1924
ApplicationManager.getApplication().executeOnPooledThread {
2025
CodeWhispererAutoTriggerService.getInstance().tryInvokeAutoTrigger(editor, CodeWhispererAutomatedTriggerType.Enter())
2126
}

jetbrains-core/src/software/aws/toolkits/jetbrains/services/codewhisperer/editor/CodeWhispererTypedHandler.kt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import com.intellij.openapi.editor.Editor
88
import com.intellij.openapi.project.Project
99
import com.intellij.psi.PsiFile
1010
import kotlinx.coroutines.Job
11+
import software.aws.toolkits.jetbrains.services.codewhisperer.editor.CodeWhispererEditorUtil.shouldSkipInvokingBasedOnRightContext
1112
import software.aws.toolkits.jetbrains.services.codewhisperer.service.CodeWhispererAutoTriggerService
1213
import software.aws.toolkits.jetbrains.services.codewhisperer.service.CodeWhispererAutomatedTriggerType
1314
import software.aws.toolkits.jetbrains.services.codewhisperer.util.CodeWhispererConstants
@@ -17,6 +18,11 @@ class CodeWhispererTypedHandler : TypedHandlerDelegate() {
1718
override fun charTyped(c: Char, project: Project, editor: Editor, psiFiles: PsiFile): Result {
1819
triggerOnIdle?.cancel()
1920

21+
if (shouldSkipInvokingBasedOnRightContext(editor)
22+
) {
23+
return Result.CONTINUE
24+
}
25+
2026
// Special Char
2127
if (CodeWhispererConstants.SPECIAL_CHARACTERS_LIST.contains(c.toString())) {
2228
CodeWhispererAutoTriggerService.getInstance().tryInvokeAutoTrigger(editor, CodeWhispererAutomatedTriggerType.SpecialChar(c))

jetbrains-core/src/software/aws/toolkits/jetbrains/services/codewhisperer/service/CodeWhispererUserGroupSettings.kt

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,10 @@ class CodeWhispererUserGroupSettings : PersistentStateComponent<CodeWhispererUse
7878

7979
@VisibleForTesting
8080
fun determineUserGroup(): CodeWhispererUserGroup {
81-
val group = CodeWhispererUserGroup.Control
81+
val randomNum = Math.random()
82+
val group = if (randomNum < 1 / 2.0) {
83+
CodeWhispererUserGroup.Control
84+
} else CodeWhispererUserGroup.RightContext
8285

8386
settings[USER_GROUP_KEY] = group.name
8487
version = AwsToolkit.PLUGIN_VERSION
@@ -125,7 +128,8 @@ interface CodeWhispererGroup
125128
enum class CodeWhispererUserGroup : CodeWhispererGroup {
126129
Control,
127130
CrossFile,
128-
Classifier
131+
Classifier,
132+
RightContext,
129133
}
130134

131135
enum class CodeWhispererExpThresholdGroup : CodeWhispererGroup {

jetbrains-core/tst/software/aws/toolkits/jetbrains/services/codewhisperer/CodeWhispererUserActionsTest.kt

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,11 @@ import com.intellij.openapi.actionSystem.IdeActions.ACTION_EDITOR_MOVE_LINE_STAR
1313
import com.intellij.openapi.actionSystem.IdeActions.ACTION_EDITOR_SELECT_WORD_AT_CARET
1414
import com.intellij.openapi.actionSystem.IdeActions.ACTION_EDITOR_TEXT_END_WITH_SELECTION
1515
import com.intellij.openapi.actionSystem.IdeActions.ACTION_EDITOR_TEXT_START_WITH_SELECTION
16+
import com.intellij.openapi.application.ApplicationManager
1617
import com.intellij.openapi.command.WriteCommandAction
1718
import com.intellij.openapi.editor.event.VisibleAreaEvent
1819
import com.intellij.openapi.ui.popup.JBPopup
20+
import com.intellij.testFramework.replaceService
1921
import com.intellij.testFramework.runInEdtAndWait
2022
import org.assertj.core.api.Assertions.assertThat
2123
import org.junit.Before
@@ -28,12 +30,15 @@ import org.mockito.kotlin.doReturn
2830
import org.mockito.kotlin.mock
2931
import org.mockito.kotlin.timeout
3032
import org.mockito.kotlin.verify
33+
import org.mockito.kotlin.whenever
3134
import software.aws.toolkits.jetbrains.services.codewhisperer.CodeWhispererTestUtil.javaFileName
3235
import software.aws.toolkits.jetbrains.services.codewhisperer.CodeWhispererTestUtil.pythonFileName
3336
import software.aws.toolkits.jetbrains.services.codewhisperer.CodeWhispererTestUtil.pythonTestLeftContext
3437
import software.aws.toolkits.jetbrains.services.codewhisperer.explorer.CodeWhispererExplorerActionManager
3538
import software.aws.toolkits.jetbrains.services.codewhisperer.popup.listeners.CodeWhispererScrollListener
3639
import software.aws.toolkits.jetbrains.services.codewhisperer.service.CodeWhispererInvocationStatus
40+
import software.aws.toolkits.jetbrains.services.codewhisperer.service.CodeWhispererUserGroup
41+
import software.aws.toolkits.jetbrains.services.codewhisperer.service.CodeWhispererUserGroupSettings
3742
import java.awt.Rectangle
3843

3944
class CodeWhispererUserActionsTest : CodeWhispererTestBase() {
@@ -147,6 +152,40 @@ class CodeWhispererUserActionsTest : CodeWhispererTestBase() {
147152
}
148153
}
149154

155+
@Test
156+
fun `test special characters should not trigger CodeWhisperer for user group when there is immediate right context`() {
157+
testInputSpecialCharWithRightContext("add", false)
158+
}
159+
160+
@Test
161+
fun `test special characters should trigger CodeWhisperer for user group when it is not immediate or is single }`() {
162+
testInputSpecialCharWithRightContext("}", true)
163+
testInputSpecialCharWithRightContext(" add", true)
164+
testInputSpecialCharWithRightContext("\nadd", true)
165+
}
166+
167+
private fun testInputSpecialCharWithRightContext(rightContext: String, shouldtrigger: Boolean) {
168+
val userGroupSetting = mock<CodeWhispererUserGroupSettings>()
169+
ApplicationManager.getApplication().replaceService(CodeWhispererUserGroupSettings::class.java, userGroupSetting, disposableRule.disposable)
170+
171+
whenever(userGroupSetting.getUserGroup()).thenReturn(CodeWhispererUserGroup.RightContext)
172+
173+
CodeWhispererExplorerActionManager.getInstance().setAutoEnabled(true)
174+
setFileContext(pythonFileName, "def", rightContext)
175+
projectRule.fixture.type('{')
176+
if (shouldtrigger) {
177+
val popupCaptor = argumentCaptor<JBPopup>()
178+
verify(popupManagerSpy, timeout(5000).atLeastOnce())
179+
.showPopup(any(), any(), popupCaptor.capture(), any(), any())
180+
runInEdtAndWait {
181+
popupManagerSpy.closePopup(popupCaptor.lastValue)
182+
}
183+
} else {
184+
verify(popupManagerSpy, times(0))
185+
.showPopup(any(), any(), any(), any(), any())
186+
}
187+
}
188+
150189
private fun testHittingEnterAfterWhitespaceCharsShouldTriggerCodeWhisperer(prompt: String, times: Int) {
151190
CodeWhispererExplorerActionManager.getInstance().setAutoEnabled(true)
152191
setFileContext(pythonFileName, prompt, "")

0 commit comments

Comments
 (0)