Skip to content

Instantly share code, notes, and snippets.

@pohly
Last active April 29, 2022 17:08
Show Gist options
  • Save pohly/62fb5cb1e2feb06af97fa52e8b790ecd to your computer and use it in GitHub Desktop.
Save pohly/62fb5cb1e2feb06af97fa52e8b790ecd to your computer and use it in GitHub Desktop.
configuring gopls for use with Kubernetes + Emacs over ssh
# In the Kubernetes root, create a k8s-go.work file.
(echo "go 1.18"; echo; echo "use ("; find . -name go.mod | grep -v -e '^./vendor' -e '^./_output' | sed -e 's;\(.*\)/go.mod;\t\1;' | sort; echo ")" ) >k8s-go.work
# Enable workspace mode by default:
ln -s k8s-go.work go.work
# Disable workspace mode again:
rm go.work
# Disable it for some commands which don't work with it:
GOWORK=off hack/verify-golangci-lint.sh hack/update-vendor.sh
# Make sure that golang.org/x/tools/gopls >= v0.8.0.
$ which gopls
/usr/local/bin/gopls
$ gopls version
golang.org/x/tools/gopls v0.8.0
golang.org/x/tools/[email protected] h1:a71KO95TfIvCCMQJrZBSQIGQ9lkc0kWL+dSlEdZd7HI=
# In .emacs on a different host:
;; https://github.com/golang/tools/blob/master/gopls/doc/emacs.md#configuring-lsp-mode
(require 'lsp-mode)
(add-hook 'go-mode-hook #'lsp-deferred)
(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)
;; https://github.com/golang/tools/blob/master/gopls/doc/emacs.md#configuring-gopls-via-lsp-mode
(lsp-register-custom-settings
'(
;; https://github.com/golang/tools/blob/master/gopls/doc/workspace.md#workspace-module-experimental
;; Superseeded by https://go.googlesource.com/proposal/+/master/design/45713-workspace.md in Go 1.18
;; ("gopls.experimentalWorkspaceModule" t t)
))
;; avoid running gopls through tramp, it seems unstable
;;
;; https://github.com/emacs-lsp/lsp-mode/issues/2709#issuecomment-860753973
(defun lsp-tramp-connection-over-ssh-port-forwarding ()
"Like lsp-tcp-connection, but uses SSH portforwarding."
(list
:connect (lambda (filter sentinel name environment-fn)
(let* ((host "localhost")
;; PO: passing the command via a function parameter didn't work?
(command (cons "gopls" lsp-gopls-server-args))
(_ (message "[tcp/ssh hack connect] running LSP %s " command))
(lsp-port (lsp--find-available-port host (cl-incf lsp--tcp-port)))
(command (with-parsed-tramp-file-name buffer-file-name nil
(message "[tcp/ssh hack] running LSP %s on %s / %s" command host localname)
(let* ((unix-socket (format "/tmp/lsp-ssh-portforward-%s.sock" lsp-port))
(command (list
"ssh"
;; "-vvv"
"-L" (format "%s:%s" lsp-port unix-socket)
host
"socat"
(format "unix-listen:%s" unix-socket)
(format "system:'\"cd %s && %s\"'" (file-name-directory localname) command)
)))
(message "using local command %s" command)
command)))
(final-command (if (consp command) command (list command)))
(_ (unless (executable-find (cl-first final-command))
(user-error (format "Couldn't find executable %s" (cl-first final-command)))))
(process-environment
(lsp--compute-process-environment environment-fn))
(proc (make-process :name name :connection-type 'pipe :coding 'no-conversion
:command final-command :sentinel sentinel :stderr (format "*%s::stderr*" name) :noquery t))
(tcp-proc (progn
(sleep-for 1) ; prevent a connection before SSH has run socat. Ugh.
(lsp--open-network-stream host lsp-port (concat name "::tcp")))))
;; TODO: Same :noquery issue (see above)
(set-process-query-on-exit-flag proc nil)
(set-process-query-on-exit-flag tcp-proc nil)
(set-process-filter tcp-proc filter)
(cons tcp-proc proc)))
:test? (lambda () t)))
(lsp-register-client
(make-lsp-client :new-connection (lsp-tramp-connection-over-ssh-port-forwarding)
:major-modes '(go-mode)
:remote? t
:server-id 'gopls-remote))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment