Configuring Emacs for Go Development

Setting Up Emacs for Go Development

I’ve recently started learning Go, and, as with any language, I want to configure Emacs as much as possible for Go development. After scouring the Internet, this is what I’ve come up with.

Prerequisites

You’ll need Emacs and Go installed. I’m using OS X, so I use Emacs from http://emacsformacosx.com/ and this Go package.

Once you’ve installed everything, you’ll need to set a few environment variables:

export GOROOT=/usr/local/go
export GOPATH=$HOME/Documents/projects/go
export PATH=$PATH:$GOROOT/bin

You can set $GOPATH to a different directory if you like.

go-mode

The first thing you’ll need for Go development in Emacs is go-mode. You can install this easily in Emacs 24 and up with M-x package-install go-mode.

Installing go-mode will also let you pull up documention on the standard library or third-party packages with M-x godoc. To make godoc work correctly, you’ll need to make sure Emacs has the correct values of $PATH and $GOPATH. This shouldn’t be an issue unless you’re using GUI Emacs on OS X. If you are, I’d recommend using my env-var-import package to import their values from the shell. That package will also set exec-path correctly from your $PATH, which will be important in later steps.

go-eldoc

If you’ve done any Elisp development, you’re probably familiar with eldoc. If not, eldoc shows you the argument list of the function at your point in the minibuffer. go-eldoc is a package that does this for Go. You can install it with M-x package-install go-eldoc. Once you’ve done that, you’ll need to enable it:

(defun go-mode-setup ()
  (go-eldoc-setup))

(add-hook 'go-mode-hook 'go-mode-setup)

gofmt

One of the things I really like about go is the automatic enforcement of a consistent coding style via gofmt. You could manually run this on your code, but go-mode lets you configure Emacs to run it automatically when you save a buffer:

(defun go-mode-setup ()
 (go-eldoc-setup)
 (add-hook 'before-save-hook 'gofmt-before-save))
(add-hook 'go-mode-hook 'go-mode-setup)

goimports

There is also a tool called goimports that not only formats your code like gofmt but also automatically updates all your imports. I’d recommend using this instead of gofmt.

You’ll need Mercurial installed first in order to install it. Then you can run go get code.google.com/p/go.tools/cmd/goimports. Now you can tell Emacs to use goimports instead of gofmt:

(defun go-mode-setup ()
 (go-eldoc-setup)
 (setq gofmt-command "goimports")
 (add-hook 'before-save-hook 'gofmt-before-save))
(add-hook 'go-mode-hook 'go-mode-setup)

godef

Installing go-mode will also give you godef, which will let you jump to the definition of a function like in an IDE. You can jump with M-x godef-jump and then return to the original point with M-*. I bound godef-jump to M-. for ease of use:

(defun go-mode-setup ()
 (go-eldoc-setup)
 (setq gofmt-command "goimports")
 (add-hook 'before-save-hook 'gofmt-before-save)
 (local-set-key (kbd "M-.") 'godef-jump))
(add-hook 'go-mode-hook 'go-mode-setup)

Custom compile command

You can use M-x compile to compile and test your code from Emacs. You’ll just need to set the command that compile runs:

(defun go-mode-setup ()
 (setq compile-command "go build -v && go test -v && go vet")
 (define-key (current-local-map) "\C-c\C-c" 'compile)
 (go-eldoc-setup)
 (setq gofmt-command "goimports")
 (add-hook 'before-save-hook 'gofmt-before-save)
 (local-set-key (kbd "M-.") 'godef-jump))
(add-hook 'go-mode-hook 'go-mode-setup)

I also bound C-c C-c to run compile.

Autocomplete

The last step is to set up autocompletion for Go. I use the auto-complete package for other languages, so it’s natural to use it here too. For Go-aware autocomplete, you’ll need to install the gocode tool first with go get -u -v github.com/nsf/gocode. Then you can install auto-complete with M-x package-install auto-complete and go-autocomplete with M-x package-install go-autocomplete. Once you’ve installed everything add the following to your Emacs configuration:

(require 'auto-complete-config)
(require 'go-autocomplete)

Now you’re ready to start writing some Go in Emacs!

env-var-import

env-var-import

Writing my last post on setting environment variables in GUI Emacs got me thinking about a more general solution to that problem. Rather than requiring everyone to define a function like set-exec-path-from-shell-PATH, it would be nice if there were a package that would handle importing everything for you given a list of environment variables.

To that end I wrote env-var-import. It’s available in Marmalade for easy installation: package-install env-var-import.

Once you’ve installed it, add (require 'env-var-import) to your Emacs configuration and you’re ready to do some importing!

You have two options for using it:

  1. (env-var-import): In this mode, it will only import the environment variable defined by env-var-import-exec-path-var and set exec-path to the value of that variable. env-var-import-exec-path-var defaults to to PATH but can be customized.
  2. (env-var-import '("VAR1" "VAR2")): In this mode it will still import the value of the environment variable defined in env-var-import-exec-path-var and set exec-path to that value. However, it will also import the values of VAR1 and VAR2.

Setting Environment Variables in GUI Emacs

I use Emacs for most of my development work. When I’m not SSHed into a remote machine I generally stick with the GUI Emacs. On OS X this has the downside of not inheriting environment variables from your shell.

I could work around this by using setenv, but that would require me to duplicate the values of those variables. Thankfully there is a better solution:

(defun set-exec-path-from-shell-PATH ()
  (let ((path-from-shell (shell-command-to-string "$SHELL -i -c 'echo $PATH'")))
    (setenv "PATH" path-from-shell)
    (setq exec-path (split-string path-from-shell path-separator))))

This will invoke the shell to get the value of $PATH and then use setenv to set it. Then all you need to do is add (if window-system (set-exec-path-from-shell-PATH)) to your Emacs configuration.

There is nothing special about $PATH in this regard. You could easily extend this function to set other environment variables as needed.

I originally found this code on the Clojure mailing list.

Winds of Change

When I started this blog in early 2013, I intended it to be primarily about technology. I never wrote anything in that vein, so several months later I started posting short horror stories.

I plan to keep writing fiction, but I would like to have a normal tech blog as well. As such, I’ve moved all my fiction to Library of Shadows. All new stories will be posted there.