ps: 请大家不要引发编辑器的战争,我觉得只要用的顺手,可以完成工作,大家可以自己想用什么就用什么,没有绝对的好坏之分。 第一次尝试写东西,如果有哪里有错误欢迎指出,包括病句(不要嘲讽我,rua )
使用 Emacs 开发 Golang 一段时间时间了,今天将相关配置和踩过的坑总结分享出来,本文主要介绍的并不是从零开始的配置,主要都是与 Golang 开发功能相关的配置,默认认为你已经了解如何在 Emacs 查看一些内置的函数文档,绑定快捷键等基本操作,一些基础的 Emacs 功能可以参考梦梦的 Emacs builtin modes 功能介绍。
在使用任何 编辑器 /IDE 开发时,最核心的需求无非以下几点:
Emacs 自带的 electric-pair-mode
已经足够好用,只不过默认没有开启。
(setq electric-pair-inhibit-predicate 'electric-pair-conservative-inhibit)
(add-hook 'prog-mode-hook #'electric-pair-mode)
在进行下一步配置之前,我们需要先安装 straight.el ,因为我们要用它来安装其他的第三方包。
(setq straight-check-for-modifications '(check-on-save find-when-checking))
(setq straight-vc-git-default-clone-depth 1)
(setq straight-disable-native-compile
(when (fboundp 'native-comp-available-p)
(not (native-comp-available-p))))
(defvar bootstrap-version)
(let ((bootstrap-file
(expand-file-name "straight/repos/straight.el/bootstrap.el" user-emacs-directory))
(bootstrap-version 5))
(unless (file-exists-p bootstrap-file)
(with-current-buffer
(url-retrieve-synchronously
"https://raw.githubusercontent.com/raxod502/straight.el/develop/install.el"
'silent 'inhibit-cookies)
(goto-char (point-max))
(eval-print-last-sexp)))
(load bootstrap-file nil 'nomessage))
straight.el 在安装其他包时需要访问 github,如果你的网络不够 绿色
咳咳…
可以将安装时的 github.com
替换为 github.com.cnpmjs.org
。
(defun +set-github-mirror (oldfunc &rest args)
(let ((url (apply oldfunc args)))
(replace-regexp-in-string (rx (group "github.com"))
"github.com.cnpmjs.org" url nil nil 1)))
(advice-add 'straight-vc-git--encode-url :around #'+set-github-mirror)
安装 go-mode.el ,其为我们在进行 Golang 开发时提供了相当多的常用功能。
(straight-use-package 'go-mode)
设置缩进。
(setq-default tab-width 4
indent-tabs-mode nil)
使用 goimports
代替 gofmt
在文件保存后自动格式化我们的代码
(setq gofmt-command "goimports")
(add-hook 'before-save-hook #'gofmt-before-save)
如果你使用的是 MacOS 系统,那么需要使用 exec-path-from-shell 让 Emacs 读取系统的环境变量,不然 Emacs 可能找不到你安装的 go
(when (eq system-type 'darwin)
(straight-use-package 'exec-path-from-shell)
(setq exec-path-from-shell-arguments '("-l"))
(add-hook 'after-init-hook #'exec-path-from-shell-initialize)
(with-eval-after-load "go-mode"
(with-eval-after-load "exec-path-from-shell"
(exec-path-from-shell-copy-envs '("GOPATH" "GO111MODULE" "GOPROXY")))))
go-mode 在格式化代码时如果发现错误会弹出一个 buffer,这会打乱我们的窗口布局,其实我们只需要简单的设置下自带的 flymake-mode
就可以方便的在错误之间跳转而不是通过一个单独的 buffer 查看。
(add-hook 'prog-mode-hook #'flymake-mode)
(with-eval-after-load "flymake"
(define-key flymake-mode-map (kbd "C-c C-b") 'flymake-show-diagnostics-buffer)
(define-key flymake-mode-map (kbd "M-n") 'flymake-goto-next-error)
(define-key flymake-mode-map (kbd "M-p") 'flymake-goto-prev-error))
这样就可以使用 M-n
, M-p
在错误之间移动,然后把 go-mode 自动弹出的这个 buffer 关掉。
(setq gofmt-show-errors nil)
安装 company-mode ,在补全时可以使用 C-p
C-n
或者 TAB
进行选择,回车完成补全。
(straight-use-package 'company)
(add-hook 'prog-mode-hook #'company-mode)
(setq company-tooltip-limit 10
company-tooltip-align-annotations t
company-tooltip-width-grow-only t
company-abort-manual-when-too-short t
company-require-match nil
company-backends '(company-capf)
company-tooltip-margin 0)
(with-eval-after-load "company"
(define-key company-active-map [tab] 'company-complete-common-or-cycle)
(define-key company-active-map (kbd "TAB") 'company-complete-common-or-cycle)
(define-key company-active-map (kbd "C-p") #'company-select-previous)
(define-key company-active-map (kbd "C-n") #'company-select-next))
安装 eglot ,一个 Emacs 中轻量级的 LSP 客户端,在 go-mode 中启用。
(straight-use-package 'eglot)
(add-hook 'go-mode-hook #'eglot-ensure)
(setq eglot-ignored-server-capabilites '(:documentHighlightProvider)
read-process-output-max (* 1024 1024))
eglot 使用 Emacs 内置的 project.el 管理项目,以 .git 目录作为项目的根目录,如果你的项目包含一些子项目,例如:
├── .git
├── project1
│ ├── go.mod
│ └── main.go
├── project2
│ ├── go.mod
│ └── main.go
└── project3
├── go.mod
└── main.go
如果你不想让 project1 中的代码出现在 project2 的补全中,或者在 project2 中查找定义时不想要 project1 中的定义出现在你的选择列表中时,则推荐使用 go.mod
所在的目录为项目的根目录,解决不同项目间的代码补全与跳转影响。
(with-eval-after-load "go-mode"
(with-eval-after-load "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)))
eglot 默认会使用 eldoc 显示函数等文档,但是很多时候我们不是想立即查看,为了防止文档扰乱视线,给 eldoc 设置个 delay 时间。
(setq eldoc-idle-dealy 2)
如果你想在补全函数时带有占位符,可以对项目进行单独的配置,只需要在项目根目录的 .dir-locals.el
中添加如下代码,eglot 就会在初始化 gopls 之后修改 gopls 的配置,当然这个功能依赖 yasnippet ,所以我们也需要安装它。
(straight-use-package 'yasnippet)
(add-hook 'prog-mode-hook #'yas-minor-mode)
在项目根目录中创建 .dir-locals.el
。
((go-mode
. ((eglot-workspace-configuration
. ((:gopls . (:usePlaceholders t)))))))
当然也可以在你的配置文件中默认开启,这样就不需要对项目单独设置。
(setq-default eglot-workspace-configuration
'((gopls
(usePlaceholders . t))))
另一个非常有用的 tip 是如果你的项目使用了 Build Constraints ,也可以针对项目单独修改 gopls
的配置使代码的补全与跳转完美的工作。
((go-mode
. ((eglot-workspace-configuration
. ((:gopls . (:buildFlags ["-tags=debug"])))))))
这里就不写出全局开启的示例了,而且这个功能一般不需要全局开启。
Emacs 内置的 electric-pair-mode 帮我们实现了括号匹配,project.el 可以在项目中查找文件、字符串等( project-find-file
project-search
project-switch-to-buffer
)。
在安装了 eglot 、company-mode 后实现了代码的补全、跳转等功能( xref-find-definitions
xref-find-references
),同时 eglot 配合内置的 flymake 也为我们提供了静态检查。
当然这些插件的功能远不只这些,例如 eglot 可以帮你重命名函数或变量(同时修改其引用处的名字),company-mode 不仅可以补全代码也可以补全文件路径、代码片段,在编写 Golang 时需要用到的一些工具链是不是也可以通过 elisp 管理从而达到一个命令进行安装 /更新等。
在 Emacs 中能限制你的只有你的想象力与行动力,种种强大或实用的功能不可能在一篇文章中全部介绍,剩下的就需要你自己发现或者根据自身特定需求进行扩展了。
这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。
V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。
V2EX is a community of developers, designers and creative people.