Ben's Go Emacs Setup

(This information was current as of 1/11/2016)

In case you're interested, here's what I'm using in emacs now. I think these instructions will be at least somewhat repeatable.

1. First, use an up-to-date version of emacs (I'm on the latest version, 24.5.1); I have no idea how well this will work on older versions. I install it with macports (port install emacs-app); you can also get it from homebrew (brew install emacs --cocoa). I used to use the builds from emacsformacosx.com but they seem to have memory leaks that aren't present in the macports/homebrew builds.


2. Second, set up the emacs package manager if you haven't already. Put this in .emacs:

(require 'package)
(add-to-list 'package-archives
  '("melpa" . "http://melpa.milkbox.net/packages/") t)
(add-to-list 'package-archives
    '("marmalade" .
      "http://marmalade-repo.org/packages/"))
(package-initialize)

3. Third, install the following packages with M-x package-install $PKG: go-projectile, flycheck, gotest, company, company-go. (the emacs package manager is weird; there doesn't seem to be a non-clunky way to just declare packages as dependencies of your .emacs).


4. Fourth, add this to .emacs, adjusting as necessary for your environment:

; pkg go installation
(setq exec-path (append '("/usr/local/go/bin") exec-path))
(setenv "PATH" (concat "/usr/local/go/bin:" (getenv "PATH")))

; As-you-type error highlighting
(add-hook 'after-init-hook #'global-flycheck-mode)

(defun my-go-mode-hook ()
      (setq tab-width 2 indent-tabs-mode 1)
      ; eldoc shows the signature of the function at point in the status bar.
      (go-eldoc-setup)
      (local-set-key (kbd "M-.") #'godef-jump)
      (add-hook 'before-save-hook 'gofmt-before-save)

      ; extra keybindings from https://github.com/bbatsov/prelude/blob/master/modules/prelude-go.el
      (let ((map go-mode-map))
        (define-key map (kbd "C-c a") 'go-test-current-project) ;; current package, really
        (define-key map (kbd "C-c m") 'go-test-current-file)
        (define-key map (kbd "C-c .") 'go-test-current-test)
        (define-key map (kbd "C-c b") 'go-run)))
(add-hook 'go-mode-hook 'my-go-mode-hook)

; Use projectile-test-project in place of 'compile'; assign whatever key you want.
(global-set-key [f9] 'projectile-test-project)

; "projectile" recognizes git repos (etc) as "projects" and changes settings
; as you switch between them. 
(projectile-global-mode 1)
(require 'go-projectile)
(go-projectile-tools-add-path)
(setq gofmt-command (concat go-projectile-tools-path "/bin/goimports"))

; "company" is auto-completion
(require 'company)
(require 'go-mode)
(require 'company-go)
(add-hook 'go-mode-hook (lambda ()
                          (company-mode)
                          (set (make-local-variable 'company-backends) '(company-go))))

; gotest defines a better set of error regexps for go tests, but it only
; enables them when using its own functions. Add them globally for use in
(require 'compile)
(require 'gotest)
(dolist (elt go-test-compilation-error-regexp-alist-alist)
  (add-to-list 'compilation-error-regexp-alist-alist elt))
(defun prepend-go-compilation-regexps ()
  (dolist (elt (reverse go-test-compilation-error-regexp-alist))
    (add-to-list 'compilation-error-regexp-alist elt t)))
(add-hook 'go-mode-hook 'prepend-go-compilation-regexps)

; end .emacs additions

5. Fifth, run M-x go-projectile-install-tools (one time only, although I think it would be harmless to put this in .emacs if you don't mind the extra startup time). From time to time you may need to run M-x go-projectile-update-tools.

I'm still learning all the features of this setup, but the key ones to me are:

  • Automatic per-project GOPATH (I don't even set a global $GOPATH anymore)
  • go-projectile manages its own tools so they don't interact with your regular paths
  • projectile-test-project always runs from the repo root so you can use "make test" from any directory
  • I use godef-jump (M-.) all the time
  • Flycheck runs golint, go vet, and errcheck automatically. Note that it's missing some of the exceptions we maintain in our makefile, so it will report some false positives which you can ignore.
  • Oracle commands start with C-c C-o. For example, C-c C-o r finds references to the function/variable at point.
  • gorename is bound to C-c p w