Skip to content

jjlee/rescript-mode

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

90 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Emacs major mode for ReScript, part of experimental Emacs support for the ReScript Language.

New ReScript users are advised to use one of the editors with official support from the ReScript team.

How to Get it Working

Apart from a working ReScript install and this code, for a full setup you need:

  • Strongly recommended: Emacs 27.0 or newer built with native JSON support, for LSP performance
  • The ReScript language server, for type information, compiler errors, completion, and jump to definition/find references
  • Currently, a way to format ReScript code (LSP also provides this, but there is an lsp-mode bug I need to report/fix before this works)

ReScript language server

TODO: bundle this or provide a way of auto-installing it

Install the language server globally with:

npm i -g @rescript/language-server

Vanilla Emacs

lsp-rescript provides configuration code for lsp-mode and depends on rescript-mode.

Install the following packages (e.g. using M-x package-install -- you can use things like use-package if you like of course):

  • lsp-rescript
  • lsp-ui

Add the following to your Emacs configuration code (for example to ~/.emacs):

;; Tell `rescript-mode` how to run the language server
(customize-set-variable
  'lsp-rescript-server-command
    '("rescript-language-server" "--stdio"))
(with-eval-after-load 'rescript-mode
  ;; Tell `lsp-mode` about the language server
  (require 'lsp-rescript)
  ;; Enable `lsp-mode` in rescript-mode buffers
  (add-hook 'rescript-mode-hook 'lsp-deferred)
  ;; Enable display of type information in rescript-mode buffers
  (require 'lsp-ui)
  (add-hook 'rescript-mode-hook 'lsp-ui-doc-mode))

For now: ensure that reason-mode is not installed, e.g. using package-delete (I guess reason-mode needs to change so that .re is considered Reason code but not .res).

Restart Emacs and open a ReScript .res file and you should have all the features working.

Note that vanilla Emacs handles the LSP prompt for "Start a build for this project to get the freshest data?" on opening a ReScript file in a rather unfriendly way: you have to hit TAB to see the single possible Start a Build response, then hit return.

Install the following packages (e.g. using M-x package-install:

  • rescript-mode
  • eglot

This configuration uses use-package, which you also need to install, but it shouldn't be too hard to rewrite this to not use use-package.

(use-package rescript-mode
  :hook ((rescript-mode . (lambda () (electric-indent-local-mode -1))))
  :config
  (add-to-list 'eglot-server-programs
         '(rescript-mode . ("rescript-language-server" "--stdio"))))

Doom Emacs

Default LSP

Ensure lsp is enabled in init.el.

In packages.el add:

(package! rescript-mode)
(package! lsp-rescript)

Then in config.el add:

(after! rescript-mode
  (setq lsp-rescript-server-command
        '("rescript-language-server" "--stdio"))
  ;; Tell `lsp-mode` about the `rescript-vscode` LSP server
  (require 'lsp-rescript)
  ;; Enable `lsp-mode` in rescript-mode buffers
  (add-hook 'rescript-mode-hook 'lsp-deferred)
  ;; Enable display of type information in rescript-mode buffers
  (require 'lsp-ui)
  (add-hook 'rescript-mode-hook 'lsp-ui-doc-mode))

In init.el, ensure you have the +eglot option for lsp:

(lsp +eglot)

In packages.el add:

(package! rescript-mode)

Then in config.el add:

(after! eglot
  (add-to-list 'eglot-server-programs
               '(rescript-mode . ("rescropt-language-server" "--stdio")))
  )

(add-hook 'rescript-mode-hook (lambda () (eglot-ensure)))

Spacemacs

TODO: make a configuration layer

Add lsp to the dotspacemacs-configuration-layers section of your spacemacs configuration file (SPC f e d to find that file) -- it should look something like this:

dotspacemacs-configuration-layers
'(
  lsp
  )

Add rescript-mode and lsp-rescript to the dotspacemacs-additional-packages section of your spacemacs configuration file -- it should look something like this:

dotspacemacs-additional-packages
'(
  lsp-rescript
  rescript-mode
  )

Add this to the dotspacemacs/user-config section of your spacemacs configuration file:

;; Tell `rescript-mode` how to run the language server
(customize-set-variable
  'lsp-rescript-server-command
    '("rescript-language-server" "--stdio"))
(with-eval-after-load 'rescript-mode
  ;; Tell `lsp-mode` about the lamguage server
  (require 'lsp-rescript)
  ;; All I remember is something weird happened if this wasn't there :-)
  (spacemacs|define-jump-handlers rescript-mode)
  ;; Enable `lsp-mode` in rescript-mode buffers
  (add-hook 'rescript-mode-hook 'lsp-deferred)
  ;; Enable display of type information in rescript-mode buffers
  (require 'lsp-ui)
  (add-hook 'rescript-mode-hook 'lsp-ui-doc-mode))

For now: ensure that reasonml layer is not listed in dotspacemacs-configuration-layers and reason-mode is not listed in dotspacemacs-additional-packages (I guess reason-mode needs to change so that .re is considered Reason code but not .res).

Restart spacemacs (SPC q r) and open a ReScript .res file and you should have all the features working.

Formatting and indentation

Formatting vs. Indentation

In case the distinction is unclear:

Formatting: This means that you run an Emacs command, and your whole buffer (or some section of it that you specify maybe) is magically formatted correctly -- that is, in the way that rescript format formats it.

Indentation: This means that hitting the tab key or the return key (depending how you have things configured I guess) gives you an approximation of the “official” formatting of a tool like rescript format. It’s never identical to proper formatting, but stops you having to pay attention to formatting when writing code.

Formatting

You can use a package like format-all or reformatter to get your code formatted correctly (i.e. as rescript format, soon to be renamed rescript format, formats it -- this is like gofmt for ReScript). See this thread (I've not tried either of these).

lsp-mode will make this part unnecessary when I get around to submitting a fix for an lsp-mode bug that currently causes lsp-format-buffer not to work with rescript-vscode.

Indentation

This is a terrible hack: it's lifted straight from js-mode (js.el) with little effort to adapt it to ReScript, and without any JSX support. Nevertheless, aside from JSX it seems to work OK.

For more predictability, you may prefer to use something like indent-relative or indent-relative-first-indent-point, by adding something like this in your (with-eval-after-load 'rescript mode ...:

(define my/rescript-mode-hook ()
  (setq-local indent-line-function #'indent-relative))
(add-hook 'rescript-mode-hook #'my/rescript-mode-hook)

Features

Aside from the usual font-lock and indentation provided by any language major mode, this is what is provided by LSP:

Builds and Errors

You should see any errors show up via flycheck -- for me they look like this:

Flycheck error

These errors only show up when you save.

If you don't see that, rescript build * may not be running on your project.

To provide these UI for these errors, LSP mode falls back to flymake if flycheck is not installed, so it's recommended to install the latter.

When you open a .res file in your project, you should see a prompt in Emacs in the minibuffer "Start a build for this project to get the freshest data?". You can either hit return on Start Build to say yes to that and the LSP server will start a build for you, or C-g out of that and run rescript build * yourself however you usually do that in your rescript project (in my project I run npm start).

You may find the UI here (how the Start Build option is presented) is a bit different from how I describe it depending if you're using vanilla emacs or some configuration that uses a package like ivy or helm that overrides the behaviour of completing-read.

If you never want to see this prompt you can put this in your configuration:

(custom-set-variables '(lsp-rescript-prompt-for-build nil))

If you don't see the "Start a build for this project to get the freshest data?" prompt, that may be because a build is already running somehow, or you may have a stale .bsb.lock lock file in your project.

Type Information

The configuration above enables lsp-ui-doc-mode. Hovering with the mouse or moving point to some code should give a popup like this:

Type information

Here are some other ways to see type information if you don't like it popping up automatically:

  • You can leave lsp-ui-doc-mode off and just use lsp-ui-doc-glance every time you want to see it.
  • You can use lsp-describe-thing-at-point to see the type in a window instead of in a popup overlay.

Completion

lsp-mode's completion UI is provided by company-mode, so take a look at the docs for the latter for more about that.

Jump to Definition / Find References

You can use functions like lsp-find-definition and lsp-find-references.

I believe functions like xref-find-definitions and xref-find-references also end up using LSP and seem equivalent to the LSP functions for ReScript purposes. lsp-ui-peek-find-definitions also seems equivalent to lsp-find-definition.

lsp-ui-peek-find-references is a fancier more GUI-fied version of lsp-find-references which seems to only sometimes work for me (when it does, you get a complicated overlay -- it looks like a collection of windows but is not -- in which you can preview references and navigate through them; other times it seems to jump me, sometimes inaccurately, to the definition). lsp-find-references itself seems to not find references in other files for me, I haven't yet tested to see what the behaviour is in VS code.

In spacemacs , g g (spacemacs/jump-to-definition) ends up rather indirectly using lsp-ui-peek-find-definitions (which is also bound directly to , G d). , g r is xref-find-references. , G r is lsp-ui-peek-find-references (when it shows the overlay, j and k or n and p navigate the list of references, I think h and l would navigate the file list if I ever saw one, and q quits).

Problems

If you don't see type information and errors: the ReScript compiler should be running, and in order to run it, you need the JavaScript dependencies of the project you're editing. Often those are fetched by running npm install. When you've done that, you can revert-buffer (or SPC b R in spacemacs) to reload the .res file you're looking at, which should prompt you to ask if you'd like to start a build (i.e. run the ReScript compiler).

If you run into problems with display of compilation errors (flycheck/flymake errors), try this to get rid of any stale ReScript build:

  • Kill any rescript or bsb processes
  • Remove any .bsb.lock file in your project
  • M-x revert-buffer on the .res file you're trying to edit

If you run into problems with other things, you can try killing the language server, and then if LSP doesn't automatically prompt you to restart the server, M-x lsp.

Known Issues

I've barely used this yet, so probably a lot of things are very broken!

See the github issues, but notably:

Emacs support

  • Indentation is a terrible hack and should very likely be replaced with tree-sitter-rescript and elisp-tree-sitter and tree-sitter-indent -- probably very easy? Perhaps this will also improve font-lock etc!
  • Font lock and indentation are broken for things like let \"try" = true.
  • Formatting with lsp-format-buffer is broken because it does not correctly handle the response from rescript-vscode because it uses a range like "end":{"line":1.7976931348623157e+308,"character":1.7976931348623157e+308} -- this should be easy to fix and you can use other means to do this.

Packaging issues:

  • Teach lsp-mode how to install rescript-vscode, or bundle it
  • Add spacemacs layer

Development

To run the tests, install Cask and run this in the project root directory:

cask exec ert-runner

Support

Please do not report issues related to editor support with Emacs upstream to the ReScript or rescript-vscode projects (neither on the forum nor the github issues). For now please use github issues or github discussions on this project as a place to discuss Emacs ReScript support.

Emacs is NOT SUPPORTED by the ReScript core team, nor rescript-vscode. The core ReScript team’s focus for editor support is currently on supporting VS Code and Sublime Text well. So, if you want something that you can be confident is going to work smoothly and will not go away, use one of the editors listed as supported by the core ReScript team (currently VS Code and Sublime Text). In particular, the Emacs support here depends on the LSP server from rescript-vscode and its --stdio switch, neither of which are officially supported and could be removed in a later version.

So if you have problems with Emacs and ReScript, please report your issues here, not upstream with ReScript or rescript-vscode and please don’t complain if you used ReScript with Emacs and had a bad time – if you did that, you’re going it alone and you really didn’t try the official ReScript experience – that’s unfair and a good way to annoy everybody.

About

A major mode for editing ReScript

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published