F#, Ionide, Vim and NvChad

Callum Linington
7 min readMay 3, 2023

--

I’ve been enjoying a journey into using Vim and then later, Neovim . I actually started getting interested and using it when doing git rebase -i ... , I really enjoyed the workflow of being able to replacing, then being able to jump to somewhere in a sentence then quickly remove a word and replace with another, without all the faff of CTRL+-> etc.

However…

… I’ve never been able to get the lot working together… until now. It’s been a slow learning process for me as I’ve been focusing on learning other things. So, after letting all this knowledge sink in slowly, I finally managed to get a working, and without further ado

Install Everything

Tools needed:

  1. Package Manager (chocolatey, homebrew)
  2. .NET SDK (can be done from package manager)
  3. fsautocompletedotnet tool install fsautocomplete
  4. Neovim (I do from package manager)

NvChad

You may have read in the title something about NvChad, it is the equivalent of a standard setup for neovim, everything we need to get to an IDE. Before we kick off install NvChad, please just run nvim from the command line before you do anything else, and just bathe in the minimalist glory.

Raw Neovim

Now install NvChad via this link, remember to select your operating system. Windows and Macs store the neovim config in very different places and that command points to a particular directory.

NvChad in all its glory

Anyway, now you’ve marvelled at the new look, let’s quit out of that and navigate to where it was installed, refer to the install link and O/S for the exact location

In the right location
Opened, Space + E

To see the above screen, once you’ve executed nvim you just need to hit Space+e to bring up the file tree explorer — here you hopefully see the awesomeness that is NvChad, getting you to that IDE experience in one line.

So, the next step is to use either the directional arrows or the j k keys to navigate up and down the tree view, then hit Enter on the lua directory and then again on the custom directory.

Uh Oh, Windows…

No C compiler

So, if you hit this issue and are on windows, then you can install choco install winlibs to get it. Once you’ve got the $env:PATH variable updated with the winlibs bin directory (it should do it automagically, and only be a case of executing refreshenv in the current shell, I had to do it manually :( ) it should show this

M.plugins = "custom.plugins"

All we have to do here is that that above line to this file. Simply do j j o start typing M. and you’ll see that amazing autocomplete, code lens, whatever you want to call it, pop up!

Use Tab to enumerate the list then hit Enter when you get to plugins , once you’ve fully entered that line, just hit Esc then type :w and that will save the file.

Next, we need to hit Space+e to focus back to the tree view again, it should put you back onto highlighting the chadrc.lua file (if it doesn’t, navigate to that), finally we need to press Space+a to create a new file which will be called plugins.lua

After pressing Space+e
After typing plugins.lua and hitting enter
After hitting enter

Once you’ve opened up this file, we need to add the following

local plugins = {
{
"neovim/nvim-lspconfig",
config = function ()
require "plugins.configs.lspconfig"
require "custom.configs.lspconfig"
end
},
{ "ionide/Ionide-vim" }
}
return plugins
Add custom plugins

I used the Ionide repository to find out the name I should be putting in here, but NvChad doesn’t use vim plug, it uses Lazy I believe.

Once you’ve updated the file with that text (you can simply press i when you’re in the file and type like normal) just hit Esc and :w to write the file.

Now we need to create the custom/lspconfig.lua file so we can instantiate the ionide plugin. If this is the first time you’ve seen LSP , it stands for Language Server Protocol and is a standard API that allows Language Clients (neovim, vscode) to interact in a standard way with Language Servers (C++, C, C#, F#, Rust, JavaScript, TypeScript).

create the custom plugins

Now that we’ve created that file, now we need to put custom LSPconfig in it so that when ionide loads it is connected to the LSP.

lspconfig with ionide setup
local on_attach = require("plugins.configs.lspconfig").on_attach
local capabilities = require("plugins.configs.lspconfig").capabilities

require('ionide').setup {
on_attach = on_attach,
capabilities = capabilities,
}

If you reopen neovim, you’ll see it install this plugin

Ionide being installed on next open

Next, we need an init.lua , to create this you can repeat the steps above: Space+e to focus tree view, Space+a to create a new file and name it init.lua and finally hit Enter to load that file.

In this file we need to make sure that when ever you open up an F# file that it sets the filetype to fsharp , once nvim understands it’s an F# file it can then load the appropriate LSP .

init.lua file
local autocmd = vim.api.nvim_create_autocmd

-- dont list quickfix buffers
autocmd({ "BufNewFile", "BufRead" }, {
pattern = "*.fs,*.fsx,*.fsi",
command = [[set filetype=fsharp]]
})

Uh Oh, Windows/Linux…

If you check your bashrc or profile file, if there is no mention of .dotnet/tools then this will have to be added to the path, simply add the following code

if [ -d "$HOME/.dotnet/tools" ] ; then
PATH="$HOME/.dotnet/tools:$PATH"
fi

Running F#

First thing we need to create a new F# project

$ dotnet new console -n NvimFsharp -o ./nvimfsharp -lang f#

Then we need to change directory and run nvim .

Execute nvim

When it opens, use Space+e to open the tree view, then open Program.fs and you should see FSAC loading the project in the status bar at the bottom

Loading fsproj
Loaded it

Now we can start coding…

Once you’ve written something, then you can hit Alt+h or Alt+v (depending if you want horizontal or vertical) to open up nvterm (terminal) and type dotnet run to see how it goes!

Hopefully this has illuminated a journey into using neovim and F# together. There are a lot of places to go after this to getting the environment setup how you want, but the awesomeness of this is that you get to customise and configure everything.

One of the big takeaways from this is that if you want to support other languages, the process is roughly the same. Except, with other languages (including C#) the LSP can be install via Mason by typing :Mason in neovim.

Mason

But once you’ve done this, you need to make sure that you setup the LSP custom config and and install the SDKs that support that LSP . For example, clangd seen in the screenshot above needs the clang gcc compiler; gopls will need go installed; rust-analyzer will need that installed via rust and it’s package manager. For automatically adding those to the LSP config see the documentation.

Let me know about your experience trying this out, whether I’ve mentioned everything, or forgot to mention anything!

--

--