Gopls:在 Emacs 中使用

安装 gopls

要在 Emacs 中使用 gopls,您必须首先 安装 gopls 可执行文件,并确保包含生成二进制文件的目录(即 $(go env GOBIN)$(go env GOPATH)/bin)已添加到您的 PATH 中。

选择 Emacs LSP 客户端

要在 Emacs 中使用 gopls,您需要选择并安装一个 Emacs LSP 客户端包。两个流行的客户端包是 LSP ModeEglot

LSP Mode 采用“开箱即用”的方法,集成了许多功能,并且 lsp-mode 本身提供了许多额外的行为。

Eglot 采用最小侵入式的方法,专注于与其他成熟的包进行顺畅集成。它提供了一些自己的 eglot- 命令,但默认情况下不提供额外的键绑定。

选择好要使用的客户端后,请按照软件包说明进行安装:参见 Eglot 1-2-3LSP Mode 安装

通用配置

Eglot 和 LSP Mode 都可以与 Emacs 生态系统中流行的包集成

  • 内置的 xref 包提供交叉引用。
  • 内置的 Flymake 包提供即时诊断叠加。
  • Company 模式显示代码补全候选(比内置的 completion-at-point 提供更丰富的 UI)。

Eglot 使用内置的 ElDoc 迷你模式提供文档,而 LSP Mode 默认使用其自己的 lsp-ui 模式提供文档。

Eglot 默认使用 [project] 包定位项目根目录。在 LSP Mode 中,可以通过 lsp-auto-guess-root 设置来配置此行为。

配置 LSP Mode

.emacs 中加载 LSP Mode

(require 'lsp-mode)
(add-hook 'go-mode-hook #'lsp-deferred)

;; Set up before-save hooks to format buffer and add/delete imports.
;; Make sure you don't have other gofmt/goimports hooks enabled.
(defun lsp-go-install-save-hooks ()
  (add-hook 'before-save-hook #'lsp-format-buffer t t)
  (add-hook 'before-save-hook #'lsp-organize-imports t t))
(add-hook 'go-mode-hook #'lsp-go-install-save-hooks)

通过 LSP Mode 配置 gopls

有关可用的 gopls 设置信息,请参见 设置

稳定的 gopls 设置在 lsp-mode 中有相应的配置变量。例如,(setq lsp-gopls-use-placeholders nil) 将禁用补全片段中的占位符。有关可用变量列表,请参见 lsp-go

可以通过 lsp-register-custom-settings 配置实验性设置

(lsp-register-custom-settings
 '(("gopls.completeUnimported" t t)
   ("gopls.staticcheck" t t)))

请注意,更改设置后,您必须使用例如 M-x lsp-restart-workspace 来重新启动 gopls。

配置 Eglot

.emacs 中为 Go modules 配置 project

Eglot 使用内置的 project 包来识别新打开的缓冲区的 LSP 工作区。project 包本身不了解 GOPATH 或 Go modules。幸运的是,您可以为其提供一个自定义钩子,告诉它查找最近的父级 go.mod 文件(即 Go 模块的根目录)作为项目根目录。

(require 'project)

(defun project-find-go-module (dir)
  (when-let ((root (locate-dominating-file dir "go.mod")))
    (cons 'go-module root)))

(cl-defmethod project-root ((project (head go-module)))
  (cdr project))

(add-hook 'project-find-functions #'project-find-go-module)

.emacs 中加载 Eglot

;; Optional: load other packages before eglot to enable eglot integrations.
(require 'company)
(require 'yasnippet)

(require 'go-mode)
(require 'eglot)
(add-hook 'go-mode-hook 'eglot-ensure)

;; Optional: install eglot-format-buffer as a save hook.
;; The depth of -10 places this before eglot's willSave notification,
;; so that notification reports the actual contents that will be saved.
(defun eglot-format-buffer-before-save ()
  (add-hook 'before-save-hook #'eglot-format-buffer -10 t))
(add-hook 'go-mode-hook #'eglot-format-buffer-before-save)

使用 M-x eglot-upgrade-eglot 升级到最新版本的 Eglot。

通过 Eglot 配置 gopls

有关可用的 gopls 设置信息,请参见 设置

LSP 服务器设置由 eglot-workspace-configuration 变量控制,该变量可以在 .emacs 中全局设置,也可以在项目根目录的 .dir-locals.el 文件中设置。

.emacs:

(setq-default eglot-workspace-configuration
    '((:gopls .
        ((staticcheck . t)
         (matcher . "CaseSensitive")))))

.dir-locals.el:

((nil (eglot-workspace-configuration . ((gopls . ((staticcheck . t)
                          (matcher . "CaseSensitive")))))))

使用 Eglot 组织导入

goplsgoimports 的导入组织功能作为 LSP 代码操作提供,您可以通过运行 M-x eglot-code-actions(或绑定到 eglot-code-actions 函数但您选择的任意键)并在提示时选择 Organize Imports 来按需调用它。

要在保存前自动组织导入,请添加一个钩子

(add-hook 'before-save-hook
    (lambda ()
        (call-interactively 'eglot-code-action-organize-imports))
    nil t)

故障排除

常见错误

  • 当 Emacs 提示您输入项目文件夹时,如果您正在使用 modules,则必须选择模块的根文件夹(即包含“go.mod”的目录)。如果您使用 GOPATH,请将您的 $GOPATH 选为您的文件夹。
  • Emacs 必须正确设置您的环境(PATH、GOPATH 等)。您可以运行 M-x getenv <RET> PATH <RET> 来查看您的 PATH 是否在 Emacs 中设置。如果未设置,您可以尝试从终端启动 Emacs,使用 [此包][exec-path-from-shell],或将您的 shell 配置从 .bashrc 移动到 .profile 并注销后重新登录。
  • 确保只安装了一个 LSP 客户端模式。(例如,如果使用 lsp-mode,请确保您没有同时启用 eglot。)
  • 查看 *lsp-log* 缓冲区中的错误,或运行 M-x eglot-events-buffer
  • 在 Gophers slack 的 #emacs 频道寻求帮助。Gophers slack

本文档的源代码可以在 golang.org/x/tools/gopls/doc 下找到。