You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用IntelliJ/PhpStorm脚本引擎优化GitHub Copilot补全

解决GitHub Copilot过度补全的IDE脚本方案指引

问题背景

GitHub Copilot自2021年起就存在一个被大量反馈的核心问题:当用户仅需要少量代码(比如半行)时,它会生成大段代码,用户被迫先接受整段内容再删除无用部分。推测该问题未被修复的原因是多行生成被纳入了效率统计指标。

核心需求

  • 按下Ctrl + Tab(GitHub Copilot默认补全快捷键)时,自定义脚本需等待Copilot补全代码插入IDE
  • 立即移除插入的代码,将其转为补全建议展示(要求响应速度快)
  • 通过自定义快捷键逐词接受补全,直至完成所需代码

最简实现指引

脚本基础说明

IntelliJ/PhpStorm的IDE Scripting Console官方文档示例极少且多数无法运行,第三方Groovy示例常出现导入报错,也缺少JavaScript版本示例。以下提供可行的Groovy脚本思路及代码片段,优先走最简实现路径——直接在IDE Scripting Console运行脚本。

核心脚本代码片段

import com.intellij.openapi.actionSystem.AnAction
import com.intellij.openapi.actionSystem.AnActionEvent
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.editor.actionSystem.TypedAction
import com.intellij.codeInsight.completion.CompletionService
import com.intellij.codeInsight.lookup.LookupElementBuilder
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.application.ModalityState

// 存储Copilot插入的代码内容
def insertedCode = ""
// 记录原始光标位置
def originalCaretOffset = 0

// 自定义动作:拦截Copilot补全并处理
class CopilotPartialCompleteAction extends AnAction {
    @Override
    void actionPerformed(AnActionEvent e) {
        Editor editor = e.getData(com.intellij.openapi.actionSystem.CommonDataKeys.EDITOR)
        if (!editor) return

        originalCaretOffset = editor.caretModel.offset

        // 触发原始Copilot补全动作
        def copilotAction = com.intellij.openapi.actionSystem.ActionManager.getInstance().getAction("GitHub.Copilot.Complete")
        copilotAction.actionPerformed(e)

        // 延迟捕获插入的代码(需调整延迟时间适配IDE响应)
        ApplicationManager.getApplication().invokeLater({
            def document = editor.document
            def newText = document.getText(com.intellij.openapi.util.TextRange.create(originalCaretOffset, editor.caretModel.offset))
            insertedCode = newText

            // 回退插入操作
            document.deleteString(originalCaretOffset, editor.caretModel.offset)
            editor.caretModel.moveToOffset(originalCaretOffset)

            // 将代码拆分为词级补全候选
            def words = insertedCode.split(/\s+|(?=[A-Z])|(?<=[A-Z])(?=[A-Z][a-z])/)
            // 展示补全建议
            showWordCompletions(editor, words)
        }, ModalityState.NON_MODAL)
    }

    void showWordCompletions(Editor editor, List<String> words) {
        def completionService = CompletionService.getCompletionService()
        def parameters = com.intellij.codeInsight.completion.CompletionParameters.create(editor.document, editor.caretModel.offset, editor)
        def lookup = completionService.createCompletionLookup(editor, true)
        lookup.setItems(words.collect { LookupElementBuilder.create(it) })
        lookup.show()
    }
}

// 绑定快捷键到自定义动作
def actionManager = com.intellij.openapi.actionSystem.ActionManager.getInstance()
def actionId = "CopilotPartialComplete"
actionManager.registerAction(actionId, new CopilotPartialCompleteAction())

def keymapManager = com.intellij.openapi.keymap.KeymapManager.getInstance()
def activeKeymap = keymapManager.activeKeymap
activeKeymap.addShortcut(actionId, com.intellij.openapi.keymap.ShortcutSet.fromString("ctrl TAB"))

// 绑定逐词接受快捷键
class AcceptNextWordAction extends AnAction {
    @Override
    void actionPerformed(AnActionEvent e) {
        Editor editor = e.getData(com.intellij.openapi.actionSystem.CommonDataKeys.EDITOR)
        if (!editor || insertedCode.isEmpty()) return

        def words = insertedCode.split(/\s+|(?=[A-Z])|(?<=[A-Z])(?=[A-Z][a-z])/)
        if (words.isEmpty()) return

        // 插入第一个词并更新剩余代码
        def firstWord = words[0]
        editor.document.insertString(editor.caretModel.offset, firstWord)
        editor.caretModel.moveToOffset(editor.caretModel.offset + firstWord.length())
        insertedCode = insertedCode.substring(firstWord.length()).trim()
    }
}

def acceptActionId = "AcceptNextWord"
actionManager.registerAction(acceptActionId, new AcceptNextWordAction())
activeKeymap.addShortcut(acceptActionId, com.intellij.openapi.keymap.ShortcutSet.fromString("ctrl RIGHT"))

注意事项

  • 脚本需在IDE的「Tools > IDE Scripting Console」中运行,选择Groovy作为语言
  • 由于IDE版本差异,部分API可能需要调整,若出现导入报错,可通过IDE的「Go to Class」功能确认正确的类路径
  • 运行脚本后,需重启快捷键生效(或在Keymap设置中手动确认绑定)
  • 延迟时间需根据自己的IDE响应速度调整,避免捕获不到插入的代码

内容的提问来源于stack exchange,提问作者John

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.16 05:25:37