Skip to content

Commit

Permalink
codewhisperer: suppress token infilling for auto-trigger (#3914)
Browse files Browse the repository at this point in the history
  • Loading branch information
zixlin7 authored Oct 13, 2023
1 parent 6dc2096 commit 4874197
Show file tree
Hide file tree
Showing 5 changed files with 69 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ import software.aws.toolkits.jetbrains.services.codewhisperer.language.programmi
import software.aws.toolkits.jetbrains.services.codewhisperer.model.CaretContext
import software.aws.toolkits.jetbrains.services.codewhisperer.model.CaretPosition
import software.aws.toolkits.jetbrains.services.codewhisperer.model.FileContextInfo
import software.aws.toolkits.jetbrains.services.codewhisperer.service.CodeWhispererUserGroup
import software.aws.toolkits.jetbrains.services.codewhisperer.service.CodeWhispererUserGroupSettings
import software.aws.toolkits.jetbrains.services.codewhisperer.util.CodeWhispererConstants
import software.aws.toolkits.jetbrains.services.codewhisperer.util.CodeWhispererConstants.LEFT_CONTEXT_ON_CURRENT_LINE
import java.awt.Point
Expand Down Expand Up @@ -90,6 +92,17 @@ object CodeWhispererEditorUtil {
)
}

fun shouldSkipInvokingBasedOnRightContext(editor: Editor): Boolean {
val caretContext = runReadAction { CodeWhispererEditorUtil.extractCaretContext(editor) }
val rightContextLines = caretContext.rightFileContext.split(Regex("\r?\n"))
val rightContextCurrentLine = if (rightContextLines.isEmpty()) "" else rightContextLines[0]

return CodeWhispererUserGroupSettings.getInstance().getUserGroup() == CodeWhispererUserGroup.RightContext &&
rightContextCurrentLine.isNotEmpty() &&
!rightContextCurrentLine.startsWith(" ") &&
rightContextCurrentLine.trim() != ("}")
}

/**
* Checks if the [otherRange] overlaps this TextRange. Note that the comparison is `<` because the endOffset of TextRange is exclusive.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,18 @@ import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.editor.Caret
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.editor.actionSystem.EditorActionHandler
import software.aws.toolkits.jetbrains.services.codewhisperer.editor.CodeWhispererEditorUtil.shouldSkipInvokingBasedOnRightContext
import software.aws.toolkits.jetbrains.services.codewhisperer.service.CodeWhispererAutoTriggerService
import software.aws.toolkits.jetbrains.services.codewhisperer.service.CodeWhispererAutomatedTriggerType

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

if (shouldSkipInvokingBasedOnRightContext(editor)) {
return
}

ApplicationManager.getApplication().executeOnPooledThread {
CodeWhispererAutoTriggerService.getInstance().tryInvokeAutoTrigger(editor, CodeWhispererAutomatedTriggerType.Enter())
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import com.intellij.openapi.editor.Editor
import com.intellij.openapi.project.Project
import com.intellij.psi.PsiFile
import kotlinx.coroutines.Job
import software.aws.toolkits.jetbrains.services.codewhisperer.editor.CodeWhispererEditorUtil.shouldSkipInvokingBasedOnRightContext
import software.aws.toolkits.jetbrains.services.codewhisperer.service.CodeWhispererAutoTriggerService
import software.aws.toolkits.jetbrains.services.codewhisperer.service.CodeWhispererAutomatedTriggerType
import software.aws.toolkits.jetbrains.services.codewhisperer.util.CodeWhispererConstants
Expand All @@ -17,6 +18,11 @@ class CodeWhispererTypedHandler : TypedHandlerDelegate() {
override fun charTyped(c: Char, project: Project, editor: Editor, psiFiles: PsiFile): Result {
triggerOnIdle?.cancel()

if (shouldSkipInvokingBasedOnRightContext(editor)
) {
return Result.CONTINUE
}

// Special Char
if (CodeWhispererConstants.SPECIAL_CHARACTERS_LIST.contains(c.toString())) {
CodeWhispererAutoTriggerService.getInstance().tryInvokeAutoTrigger(editor, CodeWhispererAutomatedTriggerType.SpecialChar(c))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,10 @@ class CodeWhispererUserGroupSettings : PersistentStateComponent<CodeWhispererUse

@VisibleForTesting
fun determineUserGroup(): CodeWhispererUserGroup {
val group = CodeWhispererUserGroup.Control
val randomNum = Math.random()
val group = if (randomNum < 1 / 2.0) {
CodeWhispererUserGroup.Control
} else CodeWhispererUserGroup.RightContext

settings[USER_GROUP_KEY] = group.name
version = AwsToolkit.PLUGIN_VERSION
Expand Down Expand Up @@ -125,7 +128,8 @@ interface CodeWhispererGroup
enum class CodeWhispererUserGroup : CodeWhispererGroup {
Control,
CrossFile,
Classifier
Classifier,
RightContext,
}

enum class CodeWhispererExpThresholdGroup : CodeWhispererGroup {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,11 @@ import com.intellij.openapi.actionSystem.IdeActions.ACTION_EDITOR_MOVE_LINE_STAR
import com.intellij.openapi.actionSystem.IdeActions.ACTION_EDITOR_SELECT_WORD_AT_CARET
import com.intellij.openapi.actionSystem.IdeActions.ACTION_EDITOR_TEXT_END_WITH_SELECTION
import com.intellij.openapi.actionSystem.IdeActions.ACTION_EDITOR_TEXT_START_WITH_SELECTION
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.command.WriteCommandAction
import com.intellij.openapi.editor.event.VisibleAreaEvent
import com.intellij.openapi.ui.popup.JBPopup
import com.intellij.testFramework.replaceService
import com.intellij.testFramework.runInEdtAndWait
import org.assertj.core.api.Assertions.assertThat
import org.junit.Before
Expand All @@ -28,12 +30,15 @@ import org.mockito.kotlin.doReturn
import org.mockito.kotlin.mock
import org.mockito.kotlin.timeout
import org.mockito.kotlin.verify
import org.mockito.kotlin.whenever
import software.aws.toolkits.jetbrains.services.codewhisperer.CodeWhispererTestUtil.javaFileName
import software.aws.toolkits.jetbrains.services.codewhisperer.CodeWhispererTestUtil.pythonFileName
import software.aws.toolkits.jetbrains.services.codewhisperer.CodeWhispererTestUtil.pythonTestLeftContext
import software.aws.toolkits.jetbrains.services.codewhisperer.explorer.CodeWhispererExplorerActionManager
import software.aws.toolkits.jetbrains.services.codewhisperer.popup.listeners.CodeWhispererScrollListener
import software.aws.toolkits.jetbrains.services.codewhisperer.service.CodeWhispererInvocationStatus
import software.aws.toolkits.jetbrains.services.codewhisperer.service.CodeWhispererUserGroup
import software.aws.toolkits.jetbrains.services.codewhisperer.service.CodeWhispererUserGroupSettings
import java.awt.Rectangle

class CodeWhispererUserActionsTest : CodeWhispererTestBase() {
Expand Down Expand Up @@ -147,6 +152,40 @@ class CodeWhispererUserActionsTest : CodeWhispererTestBase() {
}
}

@Test
fun `test special characters should not trigger CodeWhisperer for user group when there is immediate right context`() {
testInputSpecialCharWithRightContext("add", false)
}

@Test
fun `test special characters should trigger CodeWhisperer for user group when it is not immediate or is single }`() {
testInputSpecialCharWithRightContext("}", true)
testInputSpecialCharWithRightContext(" add", true)
testInputSpecialCharWithRightContext("\nadd", true)
}

private fun testInputSpecialCharWithRightContext(rightContext: String, shouldtrigger: Boolean) {
val userGroupSetting = mock<CodeWhispererUserGroupSettings>()
ApplicationManager.getApplication().replaceService(CodeWhispererUserGroupSettings::class.java, userGroupSetting, disposableRule.disposable)

whenever(userGroupSetting.getUserGroup()).thenReturn(CodeWhispererUserGroup.RightContext)

CodeWhispererExplorerActionManager.getInstance().setAutoEnabled(true)
setFileContext(pythonFileName, "def", rightContext)
projectRule.fixture.type('{')
if (shouldtrigger) {
val popupCaptor = argumentCaptor<JBPopup>()
verify(popupManagerSpy, timeout(5000).atLeastOnce())
.showPopup(any(), any(), popupCaptor.capture(), any(), any())
runInEdtAndWait {
popupManagerSpy.closePopup(popupCaptor.lastValue)
}
} else {
verify(popupManagerSpy, times(0))
.showPopup(any(), any(), any(), any(), any())
}
}

private fun testHittingEnterAfterWhitespaceCharsShouldTriggerCodeWhisperer(prompt: String, times: Int) {
CodeWhispererExplorerActionManager.getInstance().setAutoEnabled(true)
setFileContext(pythonFileName, prompt, "")
Expand Down

0 comments on commit 4874197

Please sign in to comment.