So you want to code in F# with Emacs?
I can relate, I also love both. Oh dear, if I love them!
So, presto! Let’s make Emacs your next F# IDE!
You need 4 components:
Package | Purpose |
---|---|
fsautocomplete | The F# LSP Server |
Eglot | The Language Server Protocol client |
eglot-fsharp | Integrates Eglot with fsharp-mode .It provides completion, syntax checking and the like. |
fsharp-mode | The major mode responsible for syntax highlighting and indentation |
Optionally, you might also want to have:
Package | Purpose |
---|---|
corfu.el | It provides a pop-up for IntelliSense |
(use-package eglot
:ensure t)
M-x Eglot, Emacs Polyglot — or just Eglot —
is the official Language Server Protocol client for Emacs. It is built-in since version 29.
It is not the only LSP client available. You might prefer using
lsp-mode. This post, though, covers Eglot only. If you are
interested in an lsp-mode version, drop me a message, I will find the
time to extend the post.
An LSP client is that piece of software that communicates with the underlying Language Server to provide features like auto-completion (“IntelliSense” in the Microsoft lingo), go-to-definition, find-references, and the like.
As a client, Eglot is server agnostic, but you will need a specific
package for glueing it with fsharp-mode
. The integration is provided
by eglot-fsharp, a separate package. We will see this in the next
step.
As you have imagined, LSP is based on the client-server
architecture. Therefore, an LSP client needs a corresponding running
server. As a matter of fact, a server covers one single language, so
you will need an LSP server for F#, one for Haskell and so on.
The LSP server for F# is called FsAutoComplete,
which is part of the Ionide tool family.
You can install it via dotnet tool
or let eglot-fsharp
do this for
you. I will cover both approaches.
(use-package fsharp-mode
:defer t
:ensure t)
:defer t
enhances the startup speed by delaying the loading of the package until it is actually needed.
:ensure t
conveniently downloads the package from the internet.
Once fsharp-mode
is installed, you should see F# files properly
syntax-highlighted. Indentation will also work.
Let’s connect Eglot with fsharp-mode
:
(use-package eglot-fsharp
:ensure t
:after fsharp-mode
:config
(add-hook 'fsharp-mode-hook #'eglot-ensure))
The hook makes sure that when fsharp-mode
is activated, Eglot is
also loaded.
Now, you just need to install the F# Language Server.
There are 2 ways to do this:
dotnet
.eglot-fsharp
perform the installation.For the latter:
M-x eglot
.fsharp-mode
.eglot-fsharp
to download fsautocomplete
.fsautocomplete
will be downloaded and saved in ~/.config/emacs/FsAutoComplete/netcore
.
Although this is the standard procedure, I’m not super happy with it and I prefer a different approach. Read about it in fsautocomplete installed via dotnet.
(use-package corfu
:ensure t
:init
(global-corfu-mode)
:config
(setq corfu-min-width 250
corfu-min-height 750
corfu-count 20
corfu-auto t
corfu-cycle t
corfu-separator ?\s
corfu-preview-current "insert"
corfu-scroll-margin 25
;; enable corfu on TAB
tab-always-indent 'complete
;; shows documentation after `corfu-popupinfo-delay'
corfu-popupinfo-delay '(1.25 . 0.5))
(corfu-popupinfo-mode 1)
;; Sort by input history (no need to modify `corfu-sort-function').
(with-eval-after-load 'savehist
(corfu-history-mode 1)
(add-to-list 'savehist-additional-variables 'corfu-history)) )
;; Icons
(use-package nerd-icons
:ensure t)
(use-package nerd-icons-completion
:ensure t
:after marginalia
:config
(nerd-icons-completion-marginalia-setup)
(nerd-icons-completion-mode 1))
(use-package nerd-icons-corfu
:ensure t
:after corfu
:config
(add-to-list 'corfu-margin-formatters #'nerd-icons-corfu-formatter))
What I don’t like of eglot-fsharp
installing fsautocomplete
is the
following:
eglot
the first time.fsautocomplete
will not be available from the terminal.fsautocomplete
with dotnet
would download a second
copy.Therefore, I would rather install fsautocomplete
the way the
official NuGet page recommends to:
dotnet tool install --global fsautocomplete
This stores fsautocomplete.exe
in .dotnet/tools
, which is nothing
specific to Emacs.
Now, it’s just a matter letting eglot-fsharp
know that it needn’t
download its own copy of fsautocomplete
. This is simply done by
setting the variable eglot-fsharp-server-install-dir
to nil
:
(use-package eglot-fsharp
:ensure t
:after fsharp-mode
:config
(setq eglot-fsharp-server-install-dir nil))
That should make the trick.
Now profit!
I will cover which extra functionalities are provided by these
packages in one of the next posts. I also have to learn!
Stay tuned! Happy coding.