It is easier than ever to set up Emacs for Stan development. Here’s a screen shot of the kind of thing you can expect:
The entire init.el needed to get this behavior is below (excluding the theme, which is zenburn).
Simply ensure Emacs is at least version 29 (released July 2023) and it was compiled with tree-sitter support and the elisp will do the rest
(require 'package)
(add-to-list 'package-archives '("melpa" . "https://melpa.org/packages/") t)
;; elgot is built in to emacs 29+, but some features work better if you use the
;; latest version from GNU ELPA
(add-to-list 'package-archives '("gnu-devel" . "https://elpa.gnu.org/devel/") t)
(package-initialize)
(use-package treesit
:if (treesit-available-p)
:ensure nil
:config
(setq treesit-language-source-alist
'((stan . ("https://github.com/WardBrian/tree-sitter-stan" "v0.3.0" "grammars/stan/src"))
(stanfunctions . ("https://github.com/WardBrian/tree-sitter-stan" "v0.3.0" "grammars/stanfunctions/src"))))
(unless (treesit-language-available-p 'stan)
(treesit-install-language-grammar 'stan))
(unless (treesit-language-available-p 'stanfunctions)
(treesit-install-language-grammar 'stanfunctions)))
(use-package stan-ts-mode
:requires treesit
:mode (("\\.stan\\'" . stan-ts-mode) ("\\.stanfunctions\\'" . stan-functions-ts-mode))
:defer t
:ensure t)
(defcustom bmw/stan-language-server-location
(expand-file-name (concat "bin/stan-language-server" (car exec-suffixes)) user-emacs-directory)
"Location to download the stan-language-server binary to."
:type 'file
:group 'stan)
(use-package url)
(defun bmw/download-stan-language-server (&optional force)
"Download the latest copy of the stan-language-server.
The location is determined by stan-ts-mode-language-server-location.
Argument FORCE will make the download proceed even if the file exists."
(interactive "P")
(when (or force (not (file-exists-p bmw/stan-language-server-location)))
(let*
((version
(with-temp-buffer
(url-insert-file-contents
"https://api.github.com/repos/tomatitito/stan-language-server/releases/latest")
(let ((json (json-parse-buffer)))
(gethash "tag_name" json))))
(os-tag
(pcase system-type
((or 'windows-nt 'cygwin 'ms-dos) "windows-x86_64")
('darwin (concat "macos-"
(if (string-match-p "aarch64\\|arm" system-configuration ) "aarch64" "x86_64") ))
(_ (concat "linux-"
(if (string-match-p "aarch64\\|arm" system-configuration ) "arm64" "x86_64")))))
(url
(concat
"https://github.com/tomatitito/stan-language-server/releases/download/"
version
"/stan-ls-"
version
"-"
os-tag
(car exec-suffixes)))
(file bmw/stan-language-server-location))
(make-empty-file file t)
(delete-file file)
(url-copy-file url file)
(chmod file 500))))
;; work around https://debbugs.gnu.org/cgi/bugreport.cgi?bug=69423
(assq-delete-all 'eglot package--builtins)
(assq-delete-all 'eglot package--builtin-versions)
(use-package eglot
:ensure t
:pin gnu-devel
:hook ((stan-ts-base-mode . bmw/download-stan-language-server)
(stan-ts-base-mode . eglot-ensure))
:config
(add-to-list
'eglot-server-programs
`(stan-ts-base-mode . (,bmw/stan-language-server-location "--stdio"))))
This also serves as an announcement that stan-ts-mode is now on MELPA. Thanks to @avehtari for encouraging me to finish polishing the mode and for implementing some nice features like better auto-indentation.
