Skip to content

Instantly share code, notes, and snippets.

@robfig
Last active June 13, 2023 16:08
Show Gist options
  • Save robfig/5975784 to your computer and use it in GitHub Desktop.
Save robfig/5975784 to your computer and use it in GitHub Desktop.
Emacs integration with goimports - https://github.com/bradfitz/goimports. Every time you save your Go code, 1. Add missing imports (standard library only), 2. Remove unnecessary imports, 3. Go fmt
;; OSX - Update the PATH to match that from the shell
(defun set-exec-path-from-shell-PATH ()
(let ((path-from-shell
(replace-regexp-in-string "[[:space:]\n]*$" ""
(shell-command-to-string "$SHELL -l -c 'echo $PATH'"))))
(setenv "PATH" path-from-shell)
(setq exec-path (split-string path-from-shell path-separator))))
(when (equal system-type 'darwin) (set-exec-path-from-shell-PATH))
;; Go mode
(require 'go-mode-load) ;; go/misc/emacs/go-mode.el and go-mode-load.el should be in your emacs path
(require 'go-imports) ;; the other file in this gist
;; Is goimports available?
(setq go-save-hook #'gofmt-before-save) ;; gofmt by default
(dolist (path exec-path)
(when (file-exists-p (concat path "/goimports"))
(setq go-save-hook #'goimports-before-save)))
;; Format / fix imports before every save.
(add-hook 'go-mode-hook
'(lambda()
(add-hook 'before-save-hook go-save-hook)))
;; Mostly copied from go-mode.el
;;;###autoload
(defun goimports-before-save ()
"Add this to .emacs to run gofmt on the current buffer when saving:
(add-hook 'before-save-hook 'gofmt-before-save).
Note that this will cause go-mode to get loaded the first time
you save any file, kind of defeating the point of autoloading."
(interactive)
(when (eq major-mode 'go-mode) (goimports)))
(defun goimports ()
"Formats the current buffer according to the goimports tool."
(interactive)
(let ((tmpfile (make-temp-file "gofmt" nil ".go"))
(patchbuf (get-buffer-create "*Gofmt patch*"))
(errbuf (get-buffer-create "*Gofmt Errors*"))
(coding-system-for-read 'utf-8)
(coding-system-for-write 'utf-8))
(with-current-buffer errbuf
(setq buffer-read-only nil)
(erase-buffer))
(with-current-buffer patchbuf
(erase-buffer))
(write-region nil nil tmpfile)
;; We're using errbuf for the mixed stdout and stderr output. This
;; is not an issue because gofmt -w does not produce any stdout
;; output in case of success.
(if (zerop (call-process "goimports" nil errbuf nil "-w" tmpfile))
(if (zerop (call-process-region (point-min) (point-max) "diff" nil patchbuf nil "-n" "-" tmpfile))
(progn
(kill-buffer errbuf)
(message "Buffer is already gofmted"))
(go--apply-rcs-patch patchbuf)
(kill-buffer errbuf)
(message "Applied gofmt"))
(message "Could not apply gofmt. Check errors for details")
(gofmt--process-errors (buffer-file-name) tmpfile errbuf))
(kill-buffer patchbuf)
(delete-file tmpfile)))
(provide 'go-imports)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment