匠心精神 - 良心品质腾讯认可的专业机构-IT人的高薪实战学院

咨询电话:4000806560

《Goland 插件开发指南:编写你自己的工具增强开发效率》

《Goland 插件开发指南:编写你自己的工具增强开发效率》

随着 Go 语言在近几年的火爆,越来越多的开发者开始使用 Go 语言进行开发。而这其中,Goland 成了不少开发者的首选 IDE,因为它具有强大的代码提示和重构功能,以及内置的 Go 工具链。但是,对于一些特殊需求,Goland 还是无法完全满足开发者的需求。这时,开发自己的插件就是一个不错的选择,可以大大提升开发效率。下面,我们来一起学习如何编写自己的插件。

1. 插件的基础结构

在编写插件之前,我们需要创建一个基础结构。我们可以在 Golang IDE 中使用 “File” -> “New” -> “Project” 命令创建一个新的项目,选择 “Go Plugin” 模板。这样就会生成一个包含了基本插件结构的项目。

2. 定义插件的扩展点

在项目中,我们需要定义插件所需要扩展的点。这些扩展点可以是一个新的菜单项,一个新的快捷键,或者是对某一个功能的增强等。我们需要在插件代码中通过注册监听器来监听这些扩展点。

例如,我们可以在插件初始化时通过调用 “ApplicationManager” 来获取 “Application”,然后注册一个监听器,来监视添加快捷键的操作。代码如下:

```
import com.intellij.openapi.actionSystem.ActionManager
import com.intellij.openapi.actionSystem.IdeActions
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.components.ApplicationComponent
import com.intellij.openapi.keymap.KeymapManager
import com.intellij.openapi.keymap.KeymapUtil

class MyPlugin: ApplicationComponent {

  override fun initComponent() {
    val manager = ApplicationManager.getApplication()

    manager.messageBus.connect(manager).subscribe(ApplicationManager.APPLICATION_TOPIC, object: ApplicationAdapter() {
      override fun applicationOpened(application: Application) {
        registerShortcut()
      }
    })
  }

  fun registerShortcut() {
    val keymapManager = KeymapManager.getInstance()
    val keymap = keymapManager.activeKeymap

    val actionManager = ActionManager.getInstance()
    val action = actionManager.getAction(IdeActions.ACTION_SHOW_SETTINGS)

    KeymapUtil.getAllKeymaps().forEach {
      val shortcut = keymap.getShortcut(action)
      if (shortcut == null) {
        keymap.addShortcut(IdeActions.ACTION_SHOW_SETTINGS, it, "ctrl alt S")
      }
    }
  }
}
```

这个插件会注册一个监听器,在文档打开时监听,并将快捷键 “ctrl alt S” 注册到 “Settings” 操作中。

3. 扩展 Golang 编辑器的功能

我们可以通过添加新的工具窗口,扩展编辑器的功能。例如,我们可以在编辑器中添加一个新的工具窗口,显示当前文件中的所有函数和方法。代码如下:

```
import com.intellij.openapi.actionSystem.AnAction
import com.intellij.openapi.actionSystem.AnActionEvent
import com.intellij.openapi.actionSystem.PlatformDataKeys
import com.intellij.openapi.util.text.StringUtil
import com.intellij.ui.treeStructure.SimpleNode
import com.intellij.ui.treeStructure.SimpleTree
import com.intellij.ui.treeStructure.Tree
import com.intellij.ui.treeStructure.filtered.FilteringTreeBuilder
import com.intellij.ui.treeStructure.filtered.FilteringTreeStructure

class FunctionListPanel: JPanel() {
    private var tree: SimpleTree
    private var structure: FilteringTreeStructure
    private var treeBuilder: FilteringTreeBuilder

    init {
        val layout = BorderLayout()
        layout.hgap = 5
        layout.vgap = 5
        layout.margin = Insets(5, 5, 5, 5)
        setLayout(layout)

        val rootNode = FunctionListNode()
        tree = SimpleTree()
        structure = FilteringTreeStructure(tree.model, tree.root as SimpleNode)
        treeBuilder = FilteringTreeBuilder(tree, structure, rootNode, null)

        add(BorderLayout.CENTER, JScrollPane(tree))
        treeBuilder.expand(rootNode, null)
    }

    fun setContent(fileName: String, functions: List) {
        (tree.root as FunctionListNode).fileName = fileName
        (tree.root as FunctionListNode).children = functions.map { FunctionNode(it) }

        tree.updateUI()
        treeBuilder.expand(tree.root as SimpleNode, null)
    }
}

class FunctionListNode: SimpleNode() {
    var fileName: String = ""

    override fun getChildren(): Array {
        return children.toTypedArray()
    }

    override fun getChildDescription(): String? {
        return StringUtil.getShortName(fileName)
    }
}

class FunctionNode(private val functionName: String): SimpleNode() {
    override fun getChildren(): Array {
        return arrayOf()
    }

    override fun toString(): String {
        return functionName
    }
}

class FunctionListAction: AnAction() {
    override fun actionPerformed(e: AnActionEvent) {
        val editor = e.getData(PlatformDataKeys.EDITOR) ?: return
        val psiFile = e.getData(PlatformDataKeys.PSI_FILE) ?: return

        val functions = psiFile.functions.map { it.name }
        val panel = FunctionListPanel()
        panel.setContent(psiFile.virtualFile.path, functions)

        val toolWindow = e.project?.let { ToolWindowManager.getInstance(it).getToolWindow("Function List") }
        toolWindow?.contentManager?.removeAllContents(true)
        toolWindow?.contentManager?.addContent(ContentFactory.SERVICE.getInstance().createContent(panel, "", false))
        toolWindow?.show()
    }
}
```

这个插件将在 Golang 编辑器中添加一个新的行动 “Function List”。当点击这个行动时,会在右侧的工具窗口中显示出当前文件中的所有函数。另外,我们还可以使用 Golang 的 AST 来分析代码,以实现更加高级的功能。

4. 调试插件

当我们在编写插件的时候,有时候会遇到一些问题。这时,调试插件就显得非常重要。我们可以在 IDE 中直接运行插件进行调试,或者使用远程调试工具。

如果要在 IDE 中直接运行插件进行调试,我们可以通过 “Run” -> “Debug” 命令来启动插件。如果要使用远程调试工具,我们可以通过添加一个 VM 参数来指定调试端口。例如:

```
-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005
```

然后使用远程调试工具连接调试端口进行调试即可。

总结

本文介绍了如何编写 Golang 插件,并扩展 Golang 编辑器的功能。我们可以通过添加新的工具窗口、新的菜单项或者是新的快捷键,来扩展 Golang 编辑器的功能。同时,我们还介绍了如何调试插件。当遇到问题时,调试插件就是解决问题的关键。