Mads Kjeldgaard

neovim in action

SuperCollider is my favourite programming language for music and sound and I use it all the time. The programming language comes with an IDE that is quite capable, but I for one prefer doing all of my coding in one editor (sometimes I write C++, lua and SuperCollider in one and the same project and this allows me to do it in just one editor instance) and that editor for me is NeoVim. The advantages of (Neo)Vim are many but for me the primary ones are that it is a keyboard centric tool, it’s lightweight, it is highly configurable and it works everywhere.

This in turn makes me a faster coder and makes the code itself better and more readable. When I started out with SuperCollider, the only option was to use classic vim with scvim - and while this was nice, a lot of things were missing for me and/or buggy. Then came NeoVim and in time, David Granström wrote the SCNvim plugin for NeoVim and I have been using this for all my SuperCollider coding since it came out.

I get a lot of questions about setting up NeoVim for SuperCollider usage and so this post is a collection of advice, configuration and links to help you get started with using NeoVim and SuperCollider together.

A slight detour: New to (Neo)Vim?

If you are new to using (Neo)Vim, I would strongly recommend spending time learning the basics of the program before diving into all of this stuff. It will help you immensely and make it much more fun to use NeoVim as a SuperCollider IDE, so here is some advice for the newbie Vim user:

Vim comes with a fantastic help system with a lot of documentation. The way to use it is to type :h <something> where you replace <something> with your search term. Whenever in doubt, try looking at the help system for help.

Vim comes with a small tutorial that I recommend all new users go through. You can access it by running :Tutor inside of Vim.

The vim manual can be accessed by running :h usr_toc.txt or if you want a nicely formatted version of it online: vim.help. Vim also has a reference manual which is more in the style of cheat sheets or extensive lists of commands and key bindings. My favourite if these and the one I recommend you open from time to time is :h motions.txt - reading this from time to time will turbo charge your editing speed.

This blog series is another great resurce for newbies and seasoned users alike (I read it frequently).

If you prefer videos, there are a gazillion video tutorials on YouTube and the like.

A final but very important note to new vim users is: Don’t blindly copy-paste configurations and plugins. A common mistake is to install tons of plugins and copy configurations from other people, but it is really important that you understand what each plugin does or what each line of configuration is doing to your system - otherwise you will end up with a bloated system with a ton of features and configuration that you do not know exist, let alone how to use them (I made all of these mistakes when I started out!).

Configuring NeoVim using lua

NeoVim is a fork of Vim, and I won’t get into the history of that here, but in terms of configuration this has a slightly confusing but important consequence. Originally, vim was configured using a language called vimscript (this is a nice resource for learning that language). This works perfectly fine in NeoVim, but it is frankly a nightmare to use that language for anything other than basic configuration. NeoVim solves this by allowing users to write all of their configurations and plugins using lua - a tiny, fast and frankly FUN programming language that you could potentially learn in 15 minutes (this is an overstatement but it’s good marketing).

For a guide on lua and neovim, see this.

In this post I will write all configuration using lua. To get the most out of the lua api of NeoVim, it is important that you always have the latest version of NeoVim installed (at least version 0.5 which was a major release for the lua stuff).

Plugin management

In this post I won’t go into details on how to install each plugin. This is done using a plugin manager - I highly recommend packer.nvim but you can use whatever you like.

SCNvim - A NeoVim frontend for SuperCollider

SCNvim is the plugin that makes it possible for us to work with SuperCollider inside of NeoVim.

SCNvim serves two purposes. First of all, it adds most of the features we need to have a nice working environment for writing SuperCollider code. But the second, more subtle purpose is as a lua api for SuperCollider, making it really easy to execute any SuperCollider code from keybindings and functions in Neovim.

For information on how to configure scnvim, see :h scnvim-configuration and the scnvim wiki. Also check out :h scnvim-mappings to see the default key bindings for SCNvim.

Here are some basic options you can configure for SCNvim that will take care of some of the basics.

-- vim.g.scnvim_sclang_options = {'-l', "/home/mads/.config/SuperCollider/sclang_conf_development.yaml"}

vim.g.scnvim_postwin_syntax_hl = 1

-- scnvim neovim docs !
vim.g.scnvim_scdoc = 1

-- vertical 'v' or horizontal 'h' split
vim.g.scnvim_postwin_orientation = 'v'

-- position of the post window 'right' or 'left'
vim.g.scnvim_postwin_direction = 'right'

-- default is half the terminal size for vertical and a third for horizontal
vim.g.scnvim_postwin_size = 50

-- automatically open post window on a SuperCollider error
vim.g.scnvim_postwin_auto_toggle = 1

--	-- duration of the highlight
vim.g.scnvim_eval_flash_duration = 100

-- number of flashes. A value of 0 disables this feature.
vim.g.scnvim_eval_flash_repeats = 2

-- set this variable if you don't want the "echo args" feature
-- vim.g.scnvim_echo_args = 0

-- Configure the color of the evaluation flash
vim.cmd([[ highlight SCNvimEval guifg=black guibg=white ctermfg=black ctermbg=white ]])

-- Uncomment to not use default keybindings
-- vim.g.scnvim_no_mappings = 1

Post installation

After installing SCNvim, make sure to run :call scnvim#install() so that your SuperCollider class library gets a link to the SCNvim SuperCollider classes needed for all of this to work.

To make use of the help system, tags (for definitions) and snippets (when you’ve set it up), you will need to run :SCNvimTags from time to time. This will basically map your SuperCollider class library and extensions and make NeoVim conscious of them (which also means you need to rerun this command when you add/remove classes or packages from SuperCollider to keep NeoVim up to date with those changes).

Quick tip: Go to definition

If you’ve installed SCNvim and run the SCNvimTags command and restarted NeoVim, you can easily look up definitions for SuperCollider classes. This is really useful in a lot of situations. To do this, put your cursor on a class in a SuperCollider file and press g] or C-] to open it up.

Press C-o to jump backwards to where you came from (this is an easy way to get lost in rabbit holes, be warned).

Snippets: LuaSnip

One of the productivity boosters of SCNvim is it’s ability to automatically generate code snippets. SCNvim itself supports a range of snippet engines, but these days I prefer to use LuaSnip. After you’ve installed and set up LuaSnip, add this to your scnvim configuration:

-- Use the LuaSnip snippet engine
-- SCNvim supports "ultisnips", "luasnip" and "snippets.nvim"
snippet = {
  engine = {
    name = 'luasnip',
  },
},

And then, in your LuaSnip snippet table, add the SCNvim snippets like this:

-- Add SCNvim snippets to LuaSnip
-- Note: This replaces any supercollider snippets already present in LuaSnip
require'luasnip'.add_snippets('supercollider', require'scnvim.utils'.get_snippets())

Next step is to open up a SuperCollider file and run :SCNvimTags to regenerate the snippet system. Now, restart NeoVim, open up a SuperCollider buffer and type for example SinOsc.ar and press your LuaSnip expansion keybinding (probably Control-k).

Combining auto generated snippets with custom snippets

This is a more advanced tip and not mandatory for setting up SCNvim snippets. But we’re all nerds here so why not:

Instead of completely replacing all of LuaSnip’s supercollider table, it is possible to combine it with your own custom SuperCollider snippets using a bit of lua code.

-- Append table2 to table1
local function append_to_table(table1, table2)
	for key, value in ipairs(table2) do
		table.insert(table1, value)
	end

	return table1
end

-- This table contains all of your custom SuperCollider snippets
local my_custom_sc_snipz = {

	-- Ndef
	s("ndef",{
		t("Ndef("),
		t("\\"), i(1, "name"), t(",{|"), i(2, "freq=100, amp=0.5"), t({"|", ""}),
		t("\t"), i(2, "SinOsc.ar(freq) * amp;"), t({"", ""}),
		t({"", "})"}),
		i(3, ".play"),
	}),

	-- SynthDef
	s("synthdef",{
		t("SynthDef("),
		t("\\"), i(1, "name"), t(",{|"), i(2, "out=0, amp=0.5"), t({"|", ""}),
		t("\t"), i(3, "var sig = SinOsc.ar;"), t({"", ""}),
		t("\t"), i(4, "Out.ar(out, sig)"),
		t({"", "})"}),
		i(5, ".add"),
	}),

	-- Value pattern shorthands
	s("pwh", { t("Pwhite("), c(1, {t("0.0"), rnd()}), t(", "), c(1, {t("1.0"), rnd()}), t(")")}),
	s("pbr", { t("Pbrown("), i(1, "0.0"), t(", "), i(2, "1.0"), t(", "), i(3, "0.125"), t(")")}),
	s("ps", { t("Pseq(["), i(1, "1.0"), t("], "), i(2, "inf"), t(")")}),
	s("pr", { t("Prand(["), i(1, "1.0"), t("], "), i(2, "inf"), t(")")}),
	s("pxr", { t("Prand(["), i(1, "1.0"), t("], "), i(2, "inf"), t(")")}),
	s("pw", { t("Pwrand(["), i(1, "0.5, 0.5"), t("], ["), i(2, "50.0, 100.0"), t("], "), i(3, "inf"), t(")")}),
	s("pseg", { t("Pseg("), t("["),i(1, "1.0,0.0"), t("],"), i(2, "16"), t(","), i(3, "\\lin"), t(","), i(4,"inf"), t(")")}),
	s("pstep", { t("Pstep("), t("["),i(1, "1.0,0.0"), t("],"), i(2, "16"), t(","), i(3,"inf"), t(")")}),

	-- Misc Pattern
	s("pf", { t("Pfunc({|ev| "), i(1, ""), t(" })")}),
	s("pp", { t("Ppar(["), i(1, "p,o"), t("], "), i(2, "inf"), t(")")}),
	s("ptp", { t("Ptpar(["), i(1, "0.0, p, 2.0, o"), t("], "), i(2, "inf"), t(")")}),
}

-- Now append custom snippets to table of scnvim snippets
require'luasnip'.snippets.supercollider = append_to_table(
	require'scnvim/utils'.get_snippets(),
	my_custom_sc_snipz
)

Statusline

There are many cool choices for status line plugins for NeoVim. A popular one seems to be lualine. I haven’t compared all of the NeoVim status line plugins but I use Galaxyline at the moment.

Since GalaxyLine seems to have been abandoned, I am now using LuaLine.

SCNvim comes with the ability to display the server status in the statusline of NeoVim.

To get statuslines to work, you first need to edit your SuperCollider installation’s startup.scd file and add the following:

// scnvim
if (\SCNvim.asClass.notNil) {
    Server.default.doWhenBooted {
        \SCNvim.asClass.updateStatusLine(1, \SCNvim.asClass.port);
    }
}

~And then in LuaLine, this is one way to get the information from SCNvim displayed:

local function scstatus()
	if vim.bo.filetype == "supercollider" then
		stat = vim.fn["scnvim#statusline#server_status"]()
		stat = stat:gsub("%%", "♪")
		return stat
	else
		return ""
	end
end

And then in your sections key of your lualine config, you can call this status function:

sections = {
	lualine_a = {'mode'},
	lualine_b = {'branch', 'diff', 'diagnostics'},
	lualine_c = {'filename', scstatus},
	lualine_x = {'filetype'},
	lualine_y = {'progress'},
	lualine_z = {'location'}
}

Advanced syntax highlighting: Treesitter

Treesitter is an awesome “code parser” that has great support in NeoVim. It’s basically a program that can quickly and precisely carse your code while you type it and understand’s scopes and hierarchies and can use that to make informed decisions. The main (but not only) use case for this is to have advanced syntax highlighting. What this means in practice is that treesitter understands for example what variables are local to functions, which variables are environment variables. It knows the difference between class and instance variables etc etc.

There is a tree-sitter grammar for SuperCollider(I could really use some help in maintaining this if anyone is up for it) that enables treesitter to understand SuperCollider code. To use it in NeoVim, first install neovim-treesitter, restart NeoVim and run TSInstall supercollider. This will download and compile the SuperCollider grammar.

The only configuration I recommend specifically for SuperCollider and treesitter is to disable treesitter’s indentation for SuperCollider - at the moment it is behaving strangely so best to just disable that by adding the following to your nvim-tree-sitter configuration:

indent = {
	enable = {},
	disable = {"supercollider"}
}

Side note To get the most out of tree-sitter’s delicious code highlighting, I recommend installing one of the modern, neovim-specific color schemes that have full support for it. Eg gruvbox.nvim, gruvbox-flat.nvim or onedark.nvim.

Fuzzy finding

Fuzzy finding is in my opinion one of the greatest inventions in modern programming tooling. If you’ve never used fuzzy finding before it will surely blow your mind when you use it for the first time. I highly recommend installing fzf.vim to get use of this in vim. I highly recommend installing fzf-lua to make use of this magical tool in vim.

This will enable commands like :FzfLua files to help you quickly search for files and :FzfLua live_grep to search for specific words in all of your code files (the latter needs ripgrep to be installed, see fzf.vim’s github page for more info).

I wrote a small plugin that uses fzf to fuzzy search through a bunch of SuperCollider things like Quarks, Scales, help files, Node proxies, Synthdefs etc.

It also contains a small API for creating your own fuzzy searchers for SuperCollider.

Additional SuperCollider plugins

If you want to get wild with your SuperCollider setup in NeoVim, I’ve created a plugin for assorted hacks. Some of them are silly and some are really nice. I personally use the command :SCNewQuark all the time from this plugin to generate a new SuperCollider package (this is why I am able to make and publish so/too many SuperCollider packages).

Misc plugins

I highly recommend exploring the world of NeoVim-plugins. Things are moving extremely fast at the moment because the lua API of NeoVim has made it a lot easier to create plugins. As a consequence, there’s a gazillion NeoVim plugins out there and I recommend finding and trying the ones that suit you. That said, here are a few that I use a lot with SuperCollider programming that aren’t really specific to SuperCollider:

Got questions?

If you have any questions about SuperCollider, SCNvim or generally on using SuperCollider with NeoVim, I recommend asking on the scsynth.org forum. There’s a thread on SCNvim here but also try giving the forum a search, and if you cannot find an appropriate thread for your questions simply start a new one (I’m really crap at answering emails so don’t expect support from me if you go that route).

Tags: