Roc is a strongly typed functional programming language inspired by Elm, but for variety of platforms. See https://www.roc-lang.org/.
This code is an early work in progress and I am a novice at Emacs Lisp. Polite suggestions and contributions are welcome.
roc-ts-mode is available on MELPA.
Requirements: Emacs 29.1+.
If you haven’t done so already, first enable installation of packages from MELPA.
Then, run M-x package-install RET roc-ts-mode RET.
Finally, run M-x roc-ts-install-treesit-grammar (or install the Roc tree-sitter grammar another way).
Add the following to your init.el:
(use-package roc-ts-mode
:mode ("\\.roc\\'" . roc-ts-mode)
:config
;; any configuration goes here (e.g., see below for language server integration)...
)(straight-use-package 'roc-ts-mode)Then, run M-x roc-ts-install-treesit-grammar (or install the Roc tree-sitter grammar another way).
Add the following to packages.el (use M-x doom/goto-private-packages-file, typically bound to SPC h d p d):
(package! roc-ts-mode)Then, run doom sync on the command line and restart Emacs (or use the experimental M-x doom/reload, typically bound to SPC h r r).
Finally, run M-x roc-ts-install-treesit-grammar (or install the Roc tree-sitter grammar another way).
Add the following to config.el (use M-x doom/goto-private-config-file, typically bound to SPC h d c):
(use-package! roc-ts-mode
:mode ("\\.roc\\'" . roc-ts-mode)
:config
(map! :map roc-ts-mode-map
(:localleader
"f" #'roc-ts-format
"b" #'roc-ts-build
"t" #'roc-ts-test
"r" #'roc-ts-run
"d" #'roc-ts-dev
"c" #'roc-ts-check
"e" #'roc-ts-repl
(:prefix ("s" . "roc-start")
"a" #'roc-ts-start-app
"p" #'roc-ts-start-pkg
"u" #'roc-ts-start-update)))
(set-popup-rule! (rx bol "*roc-ts-repl") :size 0.3)
;; Setup the LSP support:
;; For this to work, you'll need roc_language_server, which is distributed in
;; Roc releases, in your PATH.
(when (and (modulep! :tools lsp) (not (modulep! :tools lsp +eglot)))
(require 'lsp-mode)
(add-to-list 'lsp-language-id-configuration '(roc-ts-mode . "roc"))
(lsp-register-client (make-lsp-client :new-connection (lsp-stdio-connection "roc_language_server")
:activation-fn (lsp-activate-on "roc")
:major-modes '(roc-ts-mode)
:server-id 'roc_ls)))
(when (modulep! :tools lsp +eglot)
(set-eglot-client! 'roc-ts-mode '("roc_language_server")))
(add-hook 'roc-ts-mode-local-vars-hook #'lsp!)
;; Formatting
(set-formatter! 'roc-ts-format '("roc" "format" "--stdin" "--stdout") :modes '(roc-ts-mode))
(setq-hook! 'roc-ts-mode-local-vars-hook
+format-with 'roc-ts-format)
;; Keywords that should trigger electric indentation
(set-electric! 'roc-ts-mode :words '("else"))
;; Extra ligatures
(when (modulep! :ui ligatures +extra)
(set-ligatures! 'roc-ts-mode
:true "Bool.true" :false "Bool.false"
:not "!"
:and "&&" :or "||")
(setq-hook! 'roc-ts-mode-hook
prettify-symbols-compose-predicate #'+roc-ts-symbol-compose-p)
(defun +roc-ts-symbol-compose-p (start end match)
"Like `prettify-symbols-default-compose-p', except that if the
match is !, it checks that it's a logical NOT rather than the !
suffix operator (syntactic sugar for Task.await; see URL
`https://www.roc-lang.org/tutorial#the-!-suffix')."
(and (prettify-symbols-default-compose-p start end match)
(or (not (equal match "!"))
(and
;; character before isn't a word character
(not (eq (char-syntax (char-before start))
?w))
;; character after is a word character or open paren
(memq (char-syntax (char-after end))
'(?\( ?w))))))))See also tad-lispy’s private module for Roc.
Partially done.
After installing the package you need to install the Tree Sitter grammar. Run treesit-install-language-grammar and select roc. This should give you syntax highlighting.
Question: Should this be automated?
Relevant discussion: https://lists.gnu.org/archive/html/emacs-devel/2023-11/msg01365.html
Tree sitter is used to make some basic indentation rules that aim to be consistent with “roc format”, and there are tests to check that consistency. It’s still a work in progress, though.
NOTE: If you’re using Doom Emacs, the recommended configuration above already supports this.
You’ll need roc_language_server, which is distributed in Roc releases, in your PATH.
Emacs 29 comes with a built-in LSP client called Eglot. To integrate with it, add the following to your configuration:
(with-eval-after-load 'roc-ts-mode
(require 'eglot)
(add-to-list 'eglot-server-programs '(roc-ts-mode "roc_language_server"))
(add-hook 'roc-ts-mode-hook #'eglot-ensure))First, install lsp-mode if you haven’t. Then add the following to your configuration:
(with-eval-after-load 'roc-ts-mode
(require 'lsp-mode)
(add-to-list 'lsp-language-id-configuration '(roc-ts-mode . "roc"))
(lsp-register-client (make-lsp-client :new-connection (lsp-stdio-connection "roc_ls")
:activation-fn (lsp-activate-on "roc")
:major-modes '(roc-ts-mode)
:server-id 'roc_ls))
(add-hook 'roc-ts-mode-hook #'lsp-deferred))You can use the following keybindings to run Roc CLI commands.
| Default keybinding | Emacs roc-ts-mode command | CLI command | Description |
|---|---|---|---|
C-c C-f | roc-ts-format | roc format | Format the current buffer |
C-c C-b | roc-ts-build | roc build | Build the current file |
C-c C-t | roc-ts-test | roc test | Test the current file |
C-c C-r | roc-ts-run | roc run | Run tests in the current file (and modules it imports) |
C-c C-d | roc-ts-dev | roc dev | Check current file, then run if no errors |
C-c C-c | roc-ts-check | roc check | Check current file for errors |
C-c C-e | roc-ts-repl | roc repl | Open a Roc REPL buffer |
| N/A | roc-ts-version | roc version | Print and copy the current version of Roc |
If the roc CLI isn’t on your PATH, you can set roc-ts-program to the right executable path.
To use the experimental roc-ts-start-... commands, ~roc-start~ must be installed:
| Default keybinding | Emacs roc-ts-start command | CLI command | Description |
|---|---|---|---|
C-c C-s C-a | roc-ts-start-app | roc-start app | Start a new Roc app with roc-start |
C-c C-s C-p | roc-ts-start-pkg | roc-start repl | Start a new Roc package with roc-start |
C-c C-s C-u | roc-ts-start-update | roc-start update | Fetch the latest packages, platforms, and app stubs |
Commands like beginning-of-defun (C-M-a by default), end-of-defun (C-M-e), and mark-defun (C-M-h) are supported.
In Doom Emacs, that also means you can use the f text object (e.g., use d i f to delete the current function).
imenu (M-g i) is also supported.
Tree-sitter-based code folding is supported using the Hideshow minor mode.
GPLv3