Last week I had the crazy idea to build a basic web server with F# on my Linux system.
I’m spoiled by Vim’s language support for other languages: hover information, autocomplete, etc.
The experience is nearly as good as using VS Code. But (Neo)Vim doesn’t come with all the cruft of Microsoft’s Electron-based editor.
I thought it would be trivial to get decent language support F#. After all, I’ve already done the work of setting up the necessary plugins and configuration for Vim.
The bulk of the language support - be it for VS Code or for Vim - comes from the Language Server Protocol (LSP):
The Language Server protocol is used between a tool (the client) and a language smartness provider (the server) to integrate features like auto complete, go to definition, find all references and alike into the tool
All you need is a language server and a language client. Ideally, it comes with linting and formatting support. If not, the community will have tools available. Those work well with different Vim plugins.
F# is an open-source language, but it’s a Microsoft brainchild. As a Linux user, using F# is a painful experience compared to other languages, where Linux is a first-class citizen.
For example, setting up support for Go took me 5 minutes.
After I spend several hours doing the same for F# I want to share my experiences in the hopes of sparing others the trouble.
F# Installation
Installing F# is trivial and without hassle. You need the .NET Core framework, an open-source framework for C#, Visual Basic, and F#.
For Arch Linux, it’s as easy as downloading the dotnet-sdk
and the dotnet-runtime
with the Arch package management tool:
yay -S dotnet-sdk dotnet-runtime
(yay is awesome, you should try it as an alternative to Arch’s pacman.)
If you don’t use Arch, you can find installers for different platforms.
Language Server Protocol
F# offers multiple language servers and Vim plugins aimed at giving you a better development experience:
All are wonky or make trade-offs that I don’t like. Some don’t work at all.
Here are my experiences:
1. Ionide-vim
Ionide-vim is tightly coupled to autozimu/LanguageClient-neovim, a language client for Vim and NeoVim.
If that’s the language server client you’re using, then go for Ionide-vim.
I switched from LanguageClient-neovim to the lightweight vim-lsc by Nate Bosch. vim-lsc uses pure VimScript. LanguageClient-neovim was originally written in Python. It needs extra dependencies and was a bit slow. (It now uses Rust, so is probably faster now. But still: extra deps).
For now, I’m a happy user of vim-lsc. I don’t want to go back to LanguageClient-neovim.
(For a more in-depth guide, read LSP in Vim with the LSC Plugin.)
2. FsAutoComplete
The FsAutoComplete project (FSAC) provides a backend service for rich editing or intellisense features for editors.
This project, written in F#, aims to provide a complete suite: linting, formatting, hover information, go to definition, etc.
Unfortunately, it’s totally broken on Linux.
Additionally, the installation instructions suck if you’re an F# newbie.
If you want to give it a whirl, you need to grab a packaged release. As we’re using .NET Core, download the fsautocomplete.netcore.zip
from the releases tab and unzip it somewhere on your computer.
These are the settings I tried with vim-lsc:
let g:lsc_server_commands = {
\ 'fsharp': {
\ 'command': 'dotnet ~/.bin/FsAutoComplete/fsautocomplete.dll --background-service-enabled',
\ 'log_level': -1,
\ 'suppress_stderr': v:true,
\ 'workspace_config': {
\ 'initializationOptions': {
\ 'AutomaticWorkspaceInit': v:true,
\ },
\ 'Fsharp.dotNetRoot': '/usr/share/dotnet',
\ 'Fsharp.useSdkScripts': v:true
\ },
\}
Point the command
to the location where you extracted the FsAutoComplete binary. The intializationOptions
are my fruitless attempt to mitigate the errors I encountered:
Cached typecheck results not yet available
See issue #583. The issue is closed, but I couldn’t get FsAutoComplete working with Vim on Arch Linux.
3. vim-fsharp
vim-fsharp is a wrapper around FsAutoComplete. It uses Mono, the predecessor of the .NET Core framework which comes with its own set of problems.
For example, I got errors like fsharpi missing
, and I have no clue how to install it. I’m not the only one.
So that was a dud, too.
4. fsharp-language-server
fsharp-language-server is a straightforward implementation of the language server protocol - nothing more, nothing less.
It often sputters, as you can see looking at the open issues on GitHub.
I still get warnings when I run the language server, but it works most of the time.
If you use Arch Linux, you can install it via the AUR:
yay -S fsharp-language-server
If not, download a packaged release and extract the files.
Then run the following command from your terminal (inside the folder of the downloaded files): dotnet build -c Release
.
Search for the FSharpLanguageServer.dll
file and point your language client configuration to the file:
let g:lsc_server_commands = {
\ 'fsharp': {
\ 'command': 'dotnet ~/.bin/fsharp-language-server/src/FSharpLanguageSerer/bin/Releasen/netcoreapp3.0/FSharpLanguageServer.dll',
\ 'log_level': -1,
\ 'suppress_stderr': v:true,
\ },
\}
If you open a F# file (.fs
), you’ll still get errors like FSharp.Core.dll not found
(see issue #67), but the language server works.
Linting and Formatting
I use ALE, the “Asynchronous Lint Engine” to check the syntax of my files and to format them.
Disappointingly, there is no support for F#.
You can install the tools FsharpLint and Fantomas and run them from the command line.
dotnet tool install -g dotnet-fsharplint
dotnet tool install -g fantomas-tool
Inside Vim, you can run an external command from the command mode if you like.
When you’re inside Vim in normal mode, type this:
:execute '!fantomas %' | edit
Then hit the Enter key.
Or
:execute '!dotnet fsharplint lint %' | edit
and Enter
.
The commands will open a sub-shell that will execute the commands and then reload the current file in Vim.
I sometimes abuse Makefiles as task runners to automate running the commands. You could use something similar, for example GitHub hooks.
Conclusion
F# support is confusing to set up for Linux. It doesn’t help that there are two frameworks for cross-platform development, Mono and .NET Core.
Generally, it feels like Linux support is not as mature as I’m accustomed to.
That makes sense, since F# is a Microsoft language.
But it’s the first time that I feel like Windows users will have a leg up on programmers that use Unix.