F#, Ionide, Vim and NvChad
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:
- Package Manager (chocolatey, homebrew)
- .NET SDK (can be done from package manager)
fsautocomplete
—dotnet tool install fsautocomplete
- 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.
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.
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
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…
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
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
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).
Now that we’ve created that file, now we need to put custom LSP
config in it so that when ionide
loads it is connected to the LSP
.
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
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
.
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
.
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
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.
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!