add go-asm-mode
Gopls issue https://github.com/golang/go/issues/71754 proposes adding basic support for navigation and hover when reading Go assembly files. https://go.dev/cl/649461 adds support for Definition within Go .s files. I have tested it using go-mode and eglot with the additions below. Could they be added to go-mode.el? (There's an unfortunate 3-way chicken-and-egg relationship between eglot, gopls, and go-mode that makes bootstrapping a new feature tricky.)
;; support for Go assembly files
;;
;; Optionally add these lines to your ~/.emacs if using eglot:
;;
;; (add-hook 'go-asm-mode-hook #'eglot-ensure)
;;
;; ;; TODO(adonovan): push this upstream to eglot.
;; (setq eglot-server-programs
;; (cons '((go-asm-mode :language-id "go.s") . ("gopls")) eglot-server-programs))
(define-derived-mode go-asm-mode asm-mode "Go assembly"
"Major mode for Go assembly (.s) files."
(set (make-local-variable 'comment-start) "// ")
(set (make-local-variable 'comment-end) "")
(set (make-local-variable 'comment-use-syntax) t)
(set (make-local-variable 'comment-start-skip) "\\(//+\\)\\s *")
(set (make-local-variable 'indent-line-function) 'go-mode-indent-line)
(setq indent-tabs-mode t))
;; Add a find-file hook to recognize Go assembly.
(add-hook 'find-file-hook #'go-asm-find-file-hook)
(defun go-asm-find-file-hook ()
"A find-file hook to recognize Go assembly files."
(if (is-go-asm (buffer-file-name))
(go-asm-mode)))
(defun is-go-asm (filename)
"Reports whether `filename' ends with .s and has at least one .go
sibling in its directory."
(if (string-suffix-p ".s" filename)
(let ((directory (file-name-directory filename)))
(if directory
(cl-some (lambda (s) (string-suffix-p ".go" s))
(condition-case nil
(directory-files directory)
(error nil)))))))
This feature is supported by gopls at master, so we could usefully add this to eglot now. Let me know if there's anything I can do to help.
I'll look into it now.
I'm not thrilled about looking at all file names in the directory. Currently I'm considering using magic-mode-alist and looking for either an include of textflag.h or a match for ^TEXT.+·, i.e. the middle dot in a function name, in the first 4096 bytes or so. That should probably catch all Go assembly files with few false positives.
@adonovan Was there any benefit to setting go-asm-mode's indent-line-function to go-mode-indent-line, as opposed to the default asm-indent-line?
That should probably catch all Go assembly files with few false positives.
Except for new files being created, which wouldn't get matched by this. Hrm.
58b0c3dfc87f5ae4137ea498dc0e03adc9eeb751 adds go-asm-mode. I've held off on mentioning it in the documentation for now, though. We'll probably want to tweak it a bit first. But this should be enough for inclusion in eglot, right?
I'm not thrilled about looking at all file names in the directory.
What's the objection? I too had an esthetic objection to the code when I first wrote it, but I realized it is both accurate (because is almost exactly the definition of when the Go command will attempt to compile a file as Go assembly) and fast (or fast enough), because it requires only a readdir, on a hot directory, when opening a file with the ".s" suffix, which is a rare event.
Currently I'm considering using magic-mode-alist and looking for either an include of textflag.h or a match for ^TEXT.+·, i.e. the middle dot in a function name, in the first 4096 bytes or so. That should probably catch all Go assembly files with few false positives.
Beware that many assembly files don't include textflag.h. Also, the TEXT directive doesn't require a dot. (The dot means "prefix this symbol with the current package path".) Looking for a text directive may be a reasonable heuristic, but it won't match a newly created assembly file, of course (as you pointed out later).
Was there any benefit to setting go-asm-mode's indent-line-function to go-mode-indent-line, as opposed to the default asm-indent-line?
I don't recall, sorry; I may have copied it without thinking. It's possible it improved the indentation of comment blocks.
[58b0c3d] adds go-asm-mode. I've held off on mentioning it in the documentation for now, though. We'll probably want to tweak it a bit first. But this should be enough for inclusion in eglot, right?
Looks great. Thanks.
I'm not thrilled about looking at all file names in the directory.
What's the objection?
Primarily the unbounded O(n) cost of it. Imagine someone opening a .s file in a directory containing millions of files (for whatever absurd reason). But I've limited it to 4096 files for now, which should hopefully be fine.
Was there any benefit to setting go-asm-mode's indent-line-function to go-mode-indent-line, as opposed to the default asm-indent-line?
I don't recall, sorry; I may have copied it without thinking. It's possible it improved the indentation of comment blocks.
Actually I'm surprised that it worked at all. When I tried, it broke, because go-mode's indentation logic depends on go-mode-specific state (e.g. go-dangling-cache).
Looks great. Thanks.
Great. I shall close this issue.