A Day In The Life

とあるプログラマの備忘録

go-mode + eglotを使ってEmacsのGolang開発環境を整える

普段 Golang でサーバコードを書くときはもっぱら Emacs を使っています。Go Modules に移行してから gocode が動作しなくなったので最近はやりの LSP(Language Server Protocol) を導入することにしました。Go の Language Server は gopls 、Emacs のLSPクライアントは eglot を使います

動作確認バージョン

Emacsは25以上、Golangは1.11以上なら問題なく動作するはずです。

gopls のインストール

Go の Language Server は3種類あります。一番下の gopls が Golang の公式 Language Server です。

一長一短あるようですが公式が安心そうなので gopls をインストールします。

$ go get -u golang.org/x/tools/cmd/gopls 

goimportsのインストール

Go の import を整理してくれるコマンドもついでにインストールしておきます。

$ go get golang.org/x/tools/cmd/goimports

init.el の設定

Emacs の LSP クライアントは lsp-mode が有名ですが、動作がめちゃくちゃ重かったのと余計な表示がいっぱい出てきたのでシンプルな eglot を使うことにしました。入力補完は company-mode を使います。あと GOPATH や GOROOT など環境変数設定のために exec-path-from-shell を使います。

;; パッケージ管理サーバ
(require 'package)
(add-to-list 'package-archives '("melpa" . "http://melpa.org/packages/") t)
(add-to-list 'package-archives '("marmalade" . "http://marmalade-repo.org/packages/"))
(package-initialize)
;; パッケージ情報の更新
;(package-refresh-contents)

;; インストールするパッケージのリスト
(defvar my/favorite-packages
  '(
    go-mode
    eglot
    company
    use-package
    exec-path-from-shell
    ))
;; my/favorite-packagesからインストールしていないパッケージをインストール
(dolist (package my/favorite-packages)
  (unless (package-installed-p package)
    (package-install package)))

;; シェルに設定されている環境変数を引き継ぐ
(exec-path-from-shell-initialize)

;; eglot
;; M-.で定義ジャンプ、M-,でジャンプ先からもどる
;; eglot はデフォルトの Language Server として go-langserver を使うので golsp に変更する
;; 事前に go get -u golang.org/x/tools/cmd/gopls しておくこと
(use-package eglot
  :config
  (define-key eglot-mode-map (kbd "M-.") 'xref-find-definitions)
  (define-key eglot-mode-map (kbd "M-,") 'pop-tag-mark)
  (add-to-list 'eglot-server-programs '(go-mode . ("gopls")))
  (add-hook 'go-mode-hook 'eglot-ensure))

;; company-mode
(use-package company
  :config
  (global-company-mode)
  (setq company-idle-delay 0)
  (setq company-minimum-prefix-length 1)
  (setq completion-ignore-case t)
  (setq company-dabbrev-downcase nil)
  (setq company-selection-wrap-around t))

;; go-mode
;; global-whitespace-modeを使うときはindent-tabs-modeをnilにすること、companyが誤作動する
;; 事前に go get golang.org/x/tools/cmd/goimports しておくこと
(let ((envs '("GOROOT" "GOPATH")))
  (exec-path-from-shell-copy-envs envs))
(use-package go-mode
  :commands go-mode
  :defer t
  :init
  (add-hook 'go-mode-hook (lambda()
                            (setq indent-tabs-mode nil)
                            (setq c-basic-offset 4)
                            (setq tab-width 4)
                            ))
  :config
  (setq gofmt-command "goimports")
  (add-hook 'before-save-hook 'gofmt-before-save))

emacs-lispのコードはこちらに置いてます。

参考