gemini/20191202-emacs-go-mode-revi...

237 lines
7.7 KiB
Plaintext

# Emacs Go Mode - Revisited
A few months ago [I went over] how to set up Emacs for Go
development. Since then, I have honestly not changed a single thing
about it. Until this week.
=> https://arenzana.org/2019/01/emacs-go-mode/ I went over
# Background
Here's the thing. Something I have changed too much over the last few
months has been the vendoring mechanism I use for my Go projects. From
[Glide], I moved to [go dep], and a couple of months ago, I started
the migration to the [Go Modules], Golang's potential long-term
solution to the package management mess the community has been living
with since the inception of the language 10 years ago ([Happy Birthday
Go!]).
Go Modules changes a lot of things about the taxonomy of your
projects: vendor management, GOPATH, `go get `, etc. Just like me and
my vendor management journey, `gomode ` has also gone through several
iterations from [here], to [here], and [here].
It was time to start looking at something else with more long term
support.
=> https://github.com/Masterminds/glide Glide
=> https://github.com/golang/dep go dep
=> https://github.com/golang/go/wiki/Modules Go Modules
=> https://blog.golang.org/10years Happy Birthday Go!
=> https://github.com/nsf/gocode here
=> https://github.com/mdempsky/gocode here
=> https://github.com/stamblerre/gocode here
# LSP time
LSP (Language Server Provider) promises to do away with all these
issues by implementing a "driver" with a common editor interface and
adapting to the languages as they evolve. [Rebecca Stambler's "Go,
please stop breaking my editor"] is a must-watch if you're interested
in the topic.
To work on Go using emacs, the most logical recipe would be
[lsp-mode], [gopls], and [go-mode]. `gopls ` is the LSP-compatible
language server, `lsp-mode ` is the Emacs interface for LSP servers,
and `go-mode ` is, well, the major mode for Go (discussed [in my
previous article]).
=> https://www.youtube.com/watch?v=5Re6BHEOT_k Rebecca Stambler's "Go, please stop breaking my editor"
=> https://github.com/emacs-lsp/lsp-mode lsp-mode
=> https://github.com/golang/tools/blob/master/gopls/README.md gopls
=> https://github.com/dominikh/go-mode.el go-mode
=> https://arenzana.org/2019/01/emacs-go-mode/ in my previous article
# Go, please
In the video (May 2019), Rebecca points out that `gopls ` is still in
alpha and warns us to proceed with caution. Currently, (December 2019)
it feels more like a beta, so not too bad, but still not
perfect. `gopls ` is, however, the future for Emacs to become a solid
Golang editor. This is because it's the only language tool (beside's
Rebecca's `gocode ` that's in maintenance mode) that fully supports Go
modules that is in active development.
All I had to do to install it was this:
```
go get golang.org/x/tools/gopls@latest
```
It's important to check out the latest version since the tool is
evolving quite quickly right now.
What happens in the editor is that Emacs spawns an instance of the
server when `go-mode ` is enabled on a buffer. After this, `lsp-mode `
will attempt to connect to it. The advantage of using a server rather
than a command is that it's much quicker to respond and interface with
Emacs (it does feel snappier once the server comes up, which is also
fairly quick). The disadvantages are the security concerns of running
a server on a laptop; but to be fair, this is not supposed to be a
long-running/permanent process.
On my Emacs config, I just added the following lines:
```
(setq lsp-gopls-staticcheck t)
(setq lsp-eldoc-render-all t)
(setq lsp-gopls-complete-unimported t)
```
I just wanted those elements to be active, but feel free to edit to
taste.
# lsp-mode
`lsp-mode ` will need to be added to our Emacs set up. I copied most
of this from the `gopls ` + `lsp-mode ` set up guides adjusting it to
my needs and adding a couple of things.
```
(use-package lsp-mode
:ensure t
:commands (lsp lsp-deferred)
:hook (go-mode . 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)
;;Optional - provides fancier overlays.
(use-package lsp-ui
:ensure t
:commands lsp-ui-mode
:init
)
;;Company mode is a standard completion package that works well with lsp-mode.
;;company-lsp integrates company mode completion with lsp-mode.
;;completion-at-point also works out of the box but doesn't support snippets.
(use-package company
:ensure t
:config
(setq company-idle-delay 0)
(setq company-minimum-prefix-length 1))
(use-package company-lsp
:ensure t
:commands company-lsp)
;;Optional - provides snippet support.
(use-package yasnippet
:ensure t
:commands yas-minor-mode
:hook (go-mode . yas-minor-mode))
;;lsp-ui-doc-enable is false because I don't like the popover that shows up on the right
;;I'll change it if I want it back
(setq lsp-ui-doc-enable nil
lsp-ui-peek-enable t
lsp-ui-sideline-enable t
lsp-ui-imenu-enable t
lsp-ui-flycheck-enable t)
```
Here, I:
* Enable `lsp-mode `.
* Add hooks for package imports and buffer formatting on save.
* Enable `lsp-ui ` (to display `go-eldoc ` info, etc).
* Company for overlays.
* `yasnippet ` for snippet support.
* Some options for `lsp-ui `.
In the options I disabled `lsp-ui-doc-enable ` because it displayed
docs on both the mini buffer and on an overlay located to the right of
the buffer. I found it too distracting and decided to disable the
overlay and leave the mini buffer help.
# Old Go Mode
So what's left of my original Go mode configuration? Well, the custom
compilation stuff, line numbers, etc are still there:
```
(defun custom-go-mode ()
(display-line-numbers-mode 1))
(use-package go-mode
:defer t
:ensure t
:mode ("\\.go\\'" . go-mode)
:init
(setq compile-command "echo Building... && go build -v && echo Testing... && go test -v && echo Linter... && golint")
(setq compilation-read-command nil)
(add-hook 'go-mode-hook 'custom-go-mode)
:bind (("M-," . compile)
("M-." . godef-jump)))
(setq compilation-window-height 14)
(defun my-compilation-hook ()
(when (not (get-buffer-window "*compilation*"))
(save-selected-window
(save-excursion
(let* ((w (split-window-vertically))
(h (window-height w)))
(select-window w)
(switch-to-buffer "*compilation*")
(shrink-window (- h compilation-window-height)))))))
(add-hook 'compilation-mode-hook 'my-compilation-hook)
(global-set-key (kbd "C-c C-c") 'comment-or-uncomment-region)
(setq compilation-scroll-output t)
```
I'm not calling any of the goimports, gocode, etc as I used to, since
`gopls ` takes care of it all.
# Summary
Besides speed, there's not a whole lot of difference between my
previous and my current setups, really. The real advantage I see is
that I'm future-proofing my setup by adopting officially-supported
procedures. Just like going to Go Modules: my workflow is not
improved, but this seems to be the direction the community is going
and it will be easier for me to adopt it now than in the future.
# Caveats
The first time you open a `.go ` file, you will be prompted for the
root of the project. It will find `go.mod ` and suggest that directory
as the root. It's an extra step, but not a big deal. The interface
makes it simple enough.
This root path might have been the issue I initially hit where Emacs
would color everything red on save. I couldn't figure out what the
problem was (I assumed it was a problem with my GOPATH and Modules),
but removing my `.emacs.d ` directory and having it recreated fixed it
on my Mac. I never had the problem on my Fedora laptop.