Skip to content

Instantly share code, notes, and snippets.

@zzell
Created November 10, 2019 14:14
Show Gist options
  • Save zzell/e94035d669e55a1cde4abcf06f5c3a94 to your computer and use it in GitHub Desktop.
Save zzell/e94035d669e55a1cde4abcf06f5c3a94 to your computer and use it in GitHub Desktop.
(setq go-packages-function 'custom/go-packages-function)
(add-hook 'go-mode-hook `(lambda () (custom/go-refresh-packages t)))
(defvar custom/go-packages-cache (list))
(defun custom/go-packages-function ()
"Return packages list for 'go-packages-function'."
custom/go-packages-cache)
(defun custom/go-refresh-packages (i)
"Refresh cached packages. I used to determine if fn called interactively.
This function should be called on 'go-mode-hook'.
It finds native and vendored imports asynchronously.
It does nothing if 'custom/go-packages-cache' isn't empty, unless called interactively."
(interactive "i")
(when (or (not i) (= (length custom/go-packages-cache) 0))
(setq custom/go-packages-cache (list))
(let ((add-to-cache
'(lambda (packages)
(setq custom/go-packages-cache
(sort
(copy-sequence (append custom/go-packages-cache (delete-dups packages)))
#'string-lessp)))))
(custom/go-find-native-packages add-to-cache)
(custom/go-find-vendored-packages add-to-cache))))
(defun custom/go-find-native-packages (callback)
"Asynchronously find native packages. CALLBACK - function that receives list of packages."
(process-shell-command-stdout-list "(cd ~ && go list -e ...)" callback))
(defun custom/go-find-vendored-packages (callback)
"Asynchronously find vendored packages. CALLBACK - function that receives list of packages."
(let ((pkg (custom/go-find-current-package)))
(when pkg
(let* ((absolute-path (concat (getenv "GOPATH") "/src/" pkg))
(vendor-path (shell-command-to-string (format "find %S -type d -name 'vendor' -prune | tr -d '\n'" absolute-path))))
(unless (string= vendor-path "")
(process-shell-command-stdout-list
(format "go list -e %S/..." (replace-regexp-in-string "^.*?\/src\/" "" vendor-path))
`(lambda (packages)
(let ((unprefix (list)))
(dolist (p packages) (push (replace-regexp-in-string "^.*?\/vendor\/" "" p) unprefix))
(,callback unprefix)))))))))
(defun custom/go-find-current-package ()
"Return current package."
(let ((gosrc (concat (getenv "GOPATH") "/src/")))
(when (string-match-p gosrc default-directory)
(let ((elems (split-string (substring default-directory (length gosrc) nil) "/")))
(when (>= (length elems) 3)
(concat (nth 0 elems) "/" (nth 1 elems) "/" (nth 2 elems)))))))
(defun process-shell-command-stdout-list (cmd callback)
"Run CMD asynchronously and exec CALLBACK on exit passing stdout as list of lines."
(let ((tmp-buf (generate-new-buffer "*loading*")))
(set-process-sentinel
(start-process (number-to-string (random)) tmp-buf shell-file-name shell-command-switch cmd)
`(lambda (process signal)
(when (memq (process-status process) '(exit signal))
(with-current-buffer ,tmp-buf
(goto-char (point-min))
(let (lines)
(while (not (eobp))
(setq lines (cons (buffer-substring-no-properties
(line-beginning-position)
(line-end-position)) lines))
(forward-line 1))
(kill-buffer ,tmp-buf)
(funcall ,callback (nreverse lines)))))))))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment